From a7ceb64895ac80e38d18bd1d100760018252dec5 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Thu, 6 Oct 2022 12:50:07 +0200 Subject: [PATCH 001/143] QmlDesigner: Add arrow to MenuItem for sub menus Task-number: QDS-7867 Change-Id: Ia2c9dcf4534a5ccc2525f36720acb2d7f971abe4 Reviewed-by: Reviewed-by: Mahmoud Badri --- .../imports/StudioControls/MenuItem.qml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml index c4bbcd7cb9f..18d9b26a5c5 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/MenuItem.qml @@ -70,6 +70,17 @@ T.MenuItem { } } + arrow: T.Label { + id: arrow + x: parent.width - (StudioTheme.Values.height + arrow.width) / 2 + y: (parent.height - arrow.height) / 2 + visible: control.subMenu + text: StudioTheme.Constants.startNode + color: StudioTheme.Values.themeTextColor + font.pixelSize: 8 + font.family: StudioTheme.Constants.iconFont.family + } + background: Rectangle { implicitWidth: textLabel.implicitWidth + control.labelSpacing + shortcutLabel.implicitWidth + control.leftPadding + control.rightPadding From 4ce9ea64c0b9e469640210becbcf1c93e783645c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sivert=20Kr=C3=B8vel?= Date: Fri, 7 Oct 2022 17:45:47 +0200 Subject: [PATCH 002/143] McuSupport: Fix wrong toolchain file path There was a typo in the package creation for the arm-greenhills toolchain file. The correct filename, as found in the QUL install directory tree, should be ghs-arm.cmake Task-number: QTCREATORBUG-28184 Change-Id: Id2db26f06f996cc24b99f412370ebfeef0f67a10 Reviewed-by: Reviewed-by: Yasser Grimes Reviewed-by: Alessandro Portale --- src/plugins/mcusupport/mcusupportsdk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/mcusupport/mcusupportsdk.cpp b/src/plugins/mcusupport/mcusupportsdk.cpp index b5ef907d8fe..b0d8a36cfd1 100644 --- a/src/plugins/mcusupport/mcusupportsdk.cpp +++ b/src/plugins/mcusupport/mcusupportsdk.cpp @@ -500,7 +500,7 @@ static McuAbstractTargetFactory::Ptr createFactory(bool isLegacy, {"arm-greenhills", McuPackagePtr{new McuPackage{settingsHandler, {}, - toolchainFilePrefix / "arm-ghs.cmake", + toolchainFilePrefix / "ghs-arm.cmake", {}, {}, Constants::TOOLCHAIN_FILE_CMAKE_VARIABLE, From fcc7edc135012ed5853e7afc7c56c811d95d2464 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Thu, 6 Oct 2022 14:51:11 +0300 Subject: [PATCH 003/143] QmlDesigner: Fix applying a bundle material to a multi-selection Fixes: QDS-7830 Change-Id: Ic939b1e3b86e6931c369b1b06887284d0b23fa79 Reviewed-by: Reviewed-by: Samuel Ghinet Reviewed-by: Miikka Heikkinen --- .../materialbrowser/materialbrowserview.cpp | 81 +++++++++---------- .../materialbrowser/materialbrowserview.h | 4 +- 2 files changed, 42 insertions(+), 43 deletions(-) diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 4cedfb9260f..01ee657f848 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -145,10 +146,10 @@ WidgetInfo MaterialBrowserView::widgetInfo() connect(matBrowserBundleModel, &MaterialBrowserBundleModel::applyToSelectedTriggered, this, [&] (BundleMaterial *bundleMat, bool add) { - if (!m_selectedModel.isValid()) + if (m_selectedModels.isEmpty()) return; - m_bundleMaterialDropTarget = m_selectedModel; + m_bundleMaterialTargets = m_selectedModels; m_bundleMaterialAddToSelected = add; ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type()); @@ -201,40 +202,42 @@ void MaterialBrowserView::applyBundleMaterialToDropTarget(const ModelNode &bundl newMatNode = bundleMat; } - if (m_bundleMaterialDropTarget.isValid() - && m_bundleMaterialDropTarget.isSubclassOf("QtQuick3D.Model")) { - QmlObjectNode qmlObjNode(m_bundleMaterialDropTarget); - if (m_bundleMaterialAddToSelected) { - // TODO: unify this logic as it exist elsewhere also - auto expToList = [](const QString &exp) { - QString copy = exp; - copy = copy.remove("[").remove("]"); + // TODO: unify this logic as it exist elsewhere also + auto expToList = [](const QString &exp) { + QString copy = exp; + copy = copy.remove("[").remove("]"); - QStringList tmp = copy.split(',', Qt::SkipEmptyParts); - for (QString &str : tmp) - str = str.trimmed(); + QStringList tmp = copy.split(',', Qt::SkipEmptyParts); + for (QString &str : tmp) + str = str.trimmed(); - return tmp; - }; + return tmp; + }; - auto listToExp = [](QStringList &stringList) { - if (stringList.size() > 1) - return QString("[" + stringList.join(",") + "]"); + auto listToExp = [](QStringList &stringList) { + if (stringList.size() > 1) + return QString("[" + stringList.join(",") + "]"); - if (stringList.size() == 1) - return stringList.first(); + if (stringList.size() == 1) + return stringList.first(); - return QString(); - }; - QStringList matList = expToList(qmlObjNode.expression("materials")); - matList.append(newMatNode.id()); - QString updatedExp = listToExp(matList); - qmlObjNode.setBindingProperty("materials", updatedExp); - } else { - qmlObjNode.setBindingProperty("materials", newMatNode.id()); + return QString(); + }; + + for (const ModelNode &target : std::as_const(m_bundleMaterialTargets)) { + if (target.isValid() && target.isSubclassOf("QtQuick3D.Model")) { + QmlObjectNode qmlObjNode(target); + if (m_bundleMaterialAddToSelected) { + QStringList matList = expToList(qmlObjNode.expression("materials")); + matList.append(newMatNode.id()); + QString updatedExp = listToExp(matList); + qmlObjNode.setBindingProperty("materials", updatedExp); + } else { + qmlObjNode.setBindingProperty("materials", newMatNode.id()); + } } - m_bundleMaterialDropTarget = {}; + m_bundleMaterialTargets = {}; m_bundleMaterialAddToSelected = false; } }); @@ -301,24 +304,20 @@ void MaterialBrowserView::selectedNodesChanged(const QList &selectedN { Q_UNUSED(lastSelectedNodeList) - m_selectedModel = {}; + m_selectedModels = Utils::filtered(selectedNodeList, [](const ModelNode &node) { + return node.isSubclassOf("QtQuick3D.Model"); + }); - for (const ModelNode &node : selectedNodeList) { - if (node.isSubclassOf("QtQuick3D.Model")) { - m_selectedModel = node; - break; - } - } - - m_widget->materialBrowserModel()->setHasModelSelection(m_selectedModel.isValid()); + m_widget->materialBrowserModel()->setHasModelSelection(!m_selectedModels.isEmpty()); + // the logic below selects the material of the first selected model if auto selection is on if (!m_autoSelectModelMaterial) return; - if (selectedNodeList.size() > 1 || !m_selectedModel.isValid()) + if (selectedNodeList.size() > 1 || m_selectedModels.isEmpty()) return; - QmlObjectNode qmlObjNode(m_selectedModel); + QmlObjectNode qmlObjNode(m_selectedModels.at(0)); QString matExp = qmlObjNode.expression("materials"); if (matExp.isEmpty()) return; @@ -478,7 +477,7 @@ void MaterialBrowserView::customNotification(const AbstractView *view, const QSt } else if (identifier == "delete_selected_material") { m_widget->materialBrowserModel()->deleteSelectedMaterial(); } else if (identifier == "drop_bundle_material") { - m_bundleMaterialDropTarget = nodeList.first(); + m_bundleMaterialTargets = nodeList; ModelNode defaultMat = getBundleMaterialDefaultInstance(m_draggedBundleMaterial->type()); if (defaultMat.isValid()) diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h index d1a4c85db6d..74eaa3a6e21 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h @@ -72,8 +72,8 @@ private: ModelNode getBundleMaterialDefaultInstance(const TypeName &type); QPointer m_widget; - ModelNode m_bundleMaterialDropTarget; - ModelNode m_selectedModel; // first selected 3D model node + QList m_bundleMaterialTargets; + QList m_selectedModels; // selected 3D model nodes BundleMaterial *m_draggedBundleMaterial = nullptr; bool m_bundleMaterialAddToSelected = false; bool m_hasQuick3DImport = false; From 7319eac2835b21d9a8c3be72016071de07d608d2 Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Fri, 7 Oct 2022 19:49:52 +0300 Subject: [PATCH 004/143] Refactor Assets Library Extracted the dialogs, the context menu, and the assets view into separate qml files. Also, reordered some functions in the assets library model Task-number: QDS-7344 Change-Id: Ida21b60d30f34723c07b2659a138e14b95598421 Reviewed-by: Mahmoud Badri Reviewed-by: Miikka Heikkinen --- .../itemLibraryQmlSources/Assets.qml | 512 +----------------- .../AssetsContextMenu.qml | 120 ++++ .../itemLibraryQmlSources/AssetsView.qml | 246 +++++++++ .../ConfirmDeleteFolderDialog.qml | 92 ++++ .../itemLibraryQmlSources/NewFolderDialog.qml | 102 ++++ .../RenameFolderDialog.qml | 124 +++++ .../assetslibrary/assetslibrarymodel.cpp | 150 ++--- 7 files changed, 764 insertions(+), 582 deletions(-) create mode 100644 share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml create mode 100644 share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml create mode 100644 share/qtcreator/qmldesigner/itemLibraryQmlSources/ConfirmDeleteFolderDialog.qml create mode 100644 share/qtcreator/qmldesigner/itemLibraryQmlSources/NewFolderDialog.qml create mode 100644 share/qtcreator/qmldesigner/itemLibraryQmlSources/RenameFolderDialog.qml diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml index 6578f96b7d1..1a91e2f492b 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml @@ -46,6 +46,10 @@ Item { // Array of supported externally dropped files that trigger custom import process property var dropComplexExtFiles: [] + AssetsContextMenu { + id: contextMenu + } + function clearSearchFilter() { searchBox.clear(); @@ -125,307 +129,11 @@ Item { root.selectedAssetsChanged() } - StudioControls.Menu { - id: contextMenu - - closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape - - onOpened: { - var numSelected = Object.values(root.selectedAssets).filter(p => p).length - deleteFileItem.text = numSelected > 1 ? qsTr("Delete Files") : qsTr("Delete File") - } - - StudioControls.MenuItem { - text: qsTr("Expand All") - enabled: root.allExpandedState !== 1 - visible: root.isDirContextMenu - height: visible ? implicitHeight : 0 - onTriggered: assetsModel.toggleExpandAll(true) - } - - StudioControls.MenuItem { - text: qsTr("Collapse All") - enabled: root.allExpandedState !== 2 - visible: root.isDirContextMenu - height: visible ? implicitHeight : 0 - onTriggered: assetsModel.toggleExpandAll(false) - } - - StudioControls.MenuSeparator { - visible: root.isDirContextMenu - height: visible ? StudioTheme.Values.border : 0 - } - - StudioControls.MenuItem { - id: deleteFileItem - text: qsTr("Delete File") - visible: root.contextFilePath - height: deleteFileItem.visible ? deleteFileItem.implicitHeight : 0 - onTriggered: { - assetsModel.deleteFiles(Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p])) - } - } - - StudioControls.MenuSeparator { - visible: root.contextFilePath - height: visible ? StudioTheme.Values.border : 0 - } - - StudioControls.MenuItem { - text: qsTr("Rename Folder") - visible: root.isDirContextMenu - height: visible ? implicitHeight : 0 - onTriggered: renameFolderDialog.open() - } - - StudioControls.MenuItem { - text: qsTr("New Folder") - onTriggered: newFolderDialog.open() - } - - StudioControls.MenuItem { - text: qsTr("Delete Folder") - visible: root.isDirContextMenu - height: visible ? implicitHeight : 0 - onTriggered: { - var dirEmpty = !(root.contextDir.dirsModel && root.contextDir.dirsModel.rowCount() > 0) - && !(root.contextDir.filesModel && root.contextDir.filesModel.rowCount() > 0); - - if (dirEmpty) - assetsModel.deleteFolder(root.contextDir.dirPath) - else - confirmDeleteFolderDialog.open() - } - } - } - RegExpValidator { id: folderNameValidator regExp: /^(\w[^*/> 0 ? 5 : 0 - bottomPadding: 0 - hideHeader: dirDepth === 0 - showLeftBorder: dirDepth > 0 - expanded: dirExpanded - visible: dirVisible - expandOnClick: false - useDefaulContextMenu: false - dropEnabled: true - - onToggleExpand: { - dirExpanded = !dirExpanded - } - - onDropEnter: (drag)=> { - root.updateDropExtFiles(drag) - section.highlight = drag.accepted && root.dropSimpleExtFiles.length > 0 - } - - onDropExit: { - section.highlight = false - } - - onDrop: { - section.highlight = false - rootView.handleExtFilesDrop(root.dropSimpleExtFiles, - root.dropComplexExtFiles, - dirPath) - } - - onShowContextMenu: { - root.contextFilePath = "" - root.contextDir = model - root.isDirContextMenu = true - root.allExpandedState = assetsModel.getAllExpandedState() - contextMenu.popup() - } - - Column { - spacing: 5 - leftPadding: 5 - - Repeater { - model: dirsModel - delegate: dirSection - } - - Repeater { - model: filesModel - delegate: fileSection - } - - Text { - text: qsTr("Empty folder") - color: StudioTheme.Values.themeTextColorDisabled - font.pixelSize: 12 - visible: !(dirsModel && dirsModel.rowCount() > 0) - && !(filesModel && filesModel.rowCount() > 0) - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton - onClicked: { - root.contextFilePath = "" - root.contextDir = model - root.isDirContextMenu = true - contextMenu.popup() - } - } - } - } - } - } - - Component { - id: fileSection - - Rectangle { - width: assetsView.width - - (assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0) - height: img.height - color: root.selectedAssets[filePath] - ? StudioTheme.Values.themeInteraction - : (mouseArea.containsMouse ? StudioTheme.Values.themeSectionHeadBackground - : "transparent") - - Row { - spacing: 5 - - Image { - id: img - asynchronous: true - fillMode: Image.PreserveAspectFit - width: 48 - height: 48 - source: "image://qmldesigner_assets/" + filePath - } - - Text { - text: fileName - color: StudioTheme.Values.themeTextColor - font.pixelSize: 14 - anchors.verticalCenter: parent.verticalCenter - } - } - - readonly property string suffix: fileName.substr(-4) - readonly property bool isFont: suffix === ".ttf" || suffix === ".otf" - property bool currFileSelected: false - - MouseArea { - id: mouseArea - - property bool allowTooltip: true - - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.LeftButton | Qt.RightButton - - onExited: tooltipBackend.hideTooltip() - onEntered: allowTooltip = true - onCanceled: { - tooltipBackend.hideTooltip() - allowTooltip = true - } - onPositionChanged: tooltipBackend.reposition() - onPressed: (mouse)=> { - forceActiveFocus() - allowTooltip = false - tooltipBackend.hideTooltip() - var ctrlDown = mouse.modifiers & Qt.ControlModifier - if (mouse.button === Qt.LeftButton) { - if (!root.selectedAssets[filePath] && !ctrlDown) - root.selectedAssets = {} - currFileSelected = ctrlDown ? !root.selectedAssets[filePath] : true - root.selectedAssets[filePath] = currFileSelected - root.selectedAssetsChanged() - - if (currFileSelected) { - rootView.startDragAsset( - Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p]), - mapToGlobal(mouse.x, mouse.y)) - } - } else { - if (!root.selectedAssets[filePath] && !ctrlDown) - root.selectedAssets = {} - currFileSelected = root.selectedAssets[filePath] || !ctrlDown - root.selectedAssets[filePath] = currFileSelected - root.selectedAssetsChanged() - - root.contextFilePath = filePath - root.contextDir = model.fileDir - root.isDirContextMenu = false - - contextMenu.popup() - } - } - - onReleased: (mouse)=> { - allowTooltip = true - if (mouse.button === Qt.LeftButton) { - if (!(mouse.modifiers & Qt.ControlModifier)) - root.selectedAssets = {} - root.selectedAssets[filePath] = currFileSelected - root.selectedAssetsChanged() - } - } - - ToolTip { - visible: !isFont && mouseArea.containsMouse && !contextMenu.visible - text: filePath - delay: 1000 - } - - Timer { - interval: 1000 - running: mouseArea.containsMouse && mouseArea.allowTooltip - onTriggered: { - if (suffix === ".ttf" || suffix === ".otf") { - tooltipBackend.name = fileName - tooltipBackend.path = filePath - tooltipBackend.showTooltip() - } - } - } - } - } - } - } } } } diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml new file mode 100644 index 00000000000..5caa139651f --- /dev/null +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsContextMenu.qml @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuickDesignerTheme +import HelperWidgets as HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +StudioControls.Menu { + id: contextMenu + + closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape + + onOpened: { + var numSelected = Object.values(root.selectedAssets).filter(p => p).length + deleteFileItem.text = numSelected > 1 ? qsTr("Delete Files") : qsTr("Delete File") + } + + StudioControls.MenuItem { + text: qsTr("Expand All") + enabled: root.allExpandedState !== 1 + visible: root.isDirContextMenu + height: visible ? implicitHeight : 0 + onTriggered: assetsModel.toggleExpandAll(true) + } + + StudioControls.MenuItem { + text: qsTr("Collapse All") + enabled: root.allExpandedState !== 2 + visible: root.isDirContextMenu + height: visible ? implicitHeight : 0 + onTriggered: assetsModel.toggleExpandAll(false) + } + + StudioControls.MenuSeparator { + visible: root.isDirContextMenu + height: visible ? StudioTheme.Values.border : 0 + } + + StudioControls.MenuItem { + id: deleteFileItem + text: qsTr("Delete File") + visible: root.contextFilePath + height: deleteFileItem.visible ? deleteFileItem.implicitHeight : 0 + onTriggered: { + assetsModel.deleteFiles(Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p])) + } + } + + StudioControls.MenuSeparator { + visible: root.contextFilePath + height: visible ? StudioTheme.Values.border : 0 + } + + StudioControls.MenuItem { + text: qsTr("Rename Folder") + visible: root.isDirContextMenu + height: visible ? implicitHeight : 0 + onTriggered: renameFolderDialog.open() + + RenameFolderDialog { + id: renameFolderDialog + } + } + + StudioControls.MenuItem { + text: qsTr("New Folder") + + NewFolderDialog { + id: newFolderDialog + } + + onTriggered: newFolderDialog.open() + } + + StudioControls.MenuItem { + text: qsTr("Delete Folder") + visible: root.isDirContextMenu + height: visible ? implicitHeight : 0 + + ConfirmDeleteFolderDialog { + id: confirmDeleteFolderDialog + } + + onTriggered: { + var dirEmpty = !(root.contextDir.dirsModel && root.contextDir.dirsModel.rowCount() > 0) + && !(root.contextDir.filesModel && root.contextDir.filesModel.rowCount() > 0); + + if (dirEmpty) + assetsModel.deleteFolder(root.contextDir.dirPath) + else + confirmDeleteFolderDialog.open() + } + } +} diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml new file mode 100644 index 00000000000..e1c017814d6 --- /dev/null +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/AssetsView.qml @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuickDesignerTheme +import HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +ScrollView { // TODO: experiment using ListView instead of ScrollView + Column + id: assetsView + clip: true + interactive: assetsView.verticalScrollBarVisible && !contextMenu.opened + + Column { + Repeater { + model: assetsModel // context property + delegate: dirSection + } + + Component { + id: dirSection + + Section { + id: section + + width: assetsView.width - + (assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0) - 5 + caption: dirName + sectionHeight: 30 + sectionFontSize: 15 + leftPadding: 0 + topPadding: dirDepth > 0 ? 5 : 0 + bottomPadding: 0 + hideHeader: dirDepth === 0 + showLeftBorder: dirDepth > 0 + expanded: dirExpanded + visible: dirVisible + expandOnClick: false + useDefaulContextMenu: false + dropEnabled: true + + onToggleExpand: { + dirExpanded = !dirExpanded + } + + onDropEnter: (drag)=> { + root.updateDropExtFiles(drag) + section.highlight = drag.accepted && root.dropSimpleExtFiles.length > 0 + } + + onDropExit: { + section.highlight = false + } + + onDrop: { + section.highlight = false + rootView.handleExtFilesDrop(root.dropSimpleExtFiles, + root.dropComplexExtFiles, + dirPath) + } + + onShowContextMenu: { + root.contextFilePath = "" + root.contextDir = model + root.isDirContextMenu = true + root.allExpandedState = assetsModel.getAllExpandedState() + contextMenu.popup() + } + + Column { + spacing: 5 + leftPadding: 5 + + Repeater { + model: dirsModel + delegate: dirSection + } + + Repeater { + model: filesModel + delegate: fileSection + } + + Text { + text: qsTr("Empty folder") + color: StudioTheme.Values.themeTextColorDisabled + font.pixelSize: 12 + visible: !(dirsModel && dirsModel.rowCount() > 0) + && !(filesModel && filesModel.rowCount() > 0) + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton + onClicked: { + root.contextFilePath = "" + root.contextDir = model + root.isDirContextMenu = true + contextMenu.popup() + } + } + } + } + } + } + + Component { + id: fileSection + + Rectangle { + width: assetsView.width - + (assetsView.verticalScrollBarVisible ? assetsView.verticalThickness : 0) + height: img.height + color: root.selectedAssets[filePath] + ? StudioTheme.Values.themeInteraction + : (mouseArea.containsMouse ? StudioTheme.Values.themeSectionHeadBackground + : "transparent") + + Row { + spacing: 5 + + Image { + id: img + asynchronous: true + fillMode: Image.PreserveAspectFit + width: 48 + height: 48 + source: "image://qmldesigner_assets/" + filePath + } + + Text { + text: fileName + color: StudioTheme.Values.themeTextColor + font.pixelSize: 14 + anchors.verticalCenter: parent.verticalCenter + } + } + + readonly property string suffix: fileName.substr(-4) + readonly property bool isFont: suffix === ".ttf" || suffix === ".otf" + property bool currFileSelected: false + + MouseArea { + id: mouseArea + + property bool allowTooltip: true + + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton | Qt.RightButton + + onExited: tooltipBackend.hideTooltip() + onEntered: allowTooltip = true + onCanceled: { + tooltipBackend.hideTooltip() + allowTooltip = true + } + onPositionChanged: tooltipBackend.reposition() + onPressed: (mouse)=> { + forceActiveFocus() + allowTooltip = false + tooltipBackend.hideTooltip() + var ctrlDown = mouse.modifiers & Qt.ControlModifier + if (mouse.button === Qt.LeftButton) { + if (!root.selectedAssets[filePath] && !ctrlDown) + root.selectedAssets = {} + currFileSelected = ctrlDown ? !root.selectedAssets[filePath] : true + root.selectedAssets[filePath] = currFileSelected + root.selectedAssetsChanged() + + if (currFileSelected) { + rootView.startDragAsset( + Object.keys(root.selectedAssets).filter(p => root.selectedAssets[p]), + mapToGlobal(mouse.x, mouse.y)) + } + } else { + if (!root.selectedAssets[filePath] && !ctrlDown) + root.selectedAssets = {} + currFileSelected = root.selectedAssets[filePath] || !ctrlDown + root.selectedAssets[filePath] = currFileSelected + root.selectedAssetsChanged() + + root.contextFilePath = filePath + root.contextDir = model.fileDir + root.isDirContextMenu = false + + contextMenu.popup() + } + } + + onReleased: (mouse)=> { + allowTooltip = true + if (mouse.button === Qt.LeftButton) { + if (!(mouse.modifiers & Qt.ControlModifier)) + root.selectedAssets = {} + root.selectedAssets[filePath] = currFileSelected + root.selectedAssetsChanged() + } + } + + ToolTip { + visible: !isFont && mouseArea.containsMouse && !contextMenu.visible + text: filePath + delay: 1000 + } + + Timer { + interval: 1000 + running: mouseArea.containsMouse && mouseArea.allowTooltip + onTriggered: { + if (suffix === ".ttf" || suffix === ".otf") { + tooltipBackend.name = fileName + tooltipBackend.path = filePath + tooltipBackend.showTooltip() + } + } + } + } + } + } + } +} diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ConfirmDeleteFolderDialog.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ConfirmDeleteFolderDialog.qml new file mode 100644 index 00000000000..a4fd300975e --- /dev/null +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ConfirmDeleteFolderDialog.qml @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuickDesignerTheme +import HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +Dialog { + id: confirmDeleteFolderDialog + + title: qsTr("Folder Not Empty") + anchors.centerIn: parent + closePolicy: Popup.CloseOnEscape + implicitWidth: 300 + modal: true + + contentItem: Column { + spacing: 20 + width: parent.width + + Text { + id: folderNotEmpty + + text: qsTr("Folder \"%1\" is not empty. Delete it anyway?") + .arg(root.contextDir ? root.contextDir.dirName : "") + color: StudioTheme.Values.themeTextColor + wrapMode: Text.WordWrap + width: confirmDeleteFolderDialog.width + leftPadding: 10 + rightPadding: 10 + + Keys.onEnterPressed: btnDelete.onClicked() + Keys.onReturnPressed: btnDelete.onClicked() + } + + Text { + text: qsTr("If the folder has assets in use, deleting it might cause the project to not work correctly.") + color: StudioTheme.Values.themeTextColor + wrapMode: Text.WordWrap + width: confirmDeleteFolderDialog.width + leftPadding: 10 + rightPadding: 10 + } + + Row { + anchors.right: parent.right + Button { + id: btnDelete + + text: qsTr("Delete") + + onClicked: { + assetsModel.deleteFolder(root.contextDir.dirPath) + confirmDeleteFolderDialog.accept() + } + } + + Button { + text: qsTr("Cancel") + onClicked: confirmDeleteFolderDialog.reject() + } + } + } + + onOpened: folderNotEmpty.forceActiveFocus() +} diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/NewFolderDialog.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/NewFolderDialog.qml new file mode 100644 index 00000000000..130026ddce1 --- /dev/null +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/NewFolderDialog.qml @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuickDesignerTheme +import HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +Dialog { + id: newFolderDialog + + title: qsTr("Create New Folder") + anchors.centerIn: parent + closePolicy: Popup.CloseOnEscape + modal: true + + contentItem: Column { + spacing: 2 + + Row { + Text { + text: qsTr("Folder name: ") + anchors.verticalCenter: parent.verticalCenter + color: StudioTheme.Values.themeTextColor + } + + StudioControls.TextField { + id: folderName + + actionIndicator.visible: false + translationIndicator.visible: false + validator: folderNameValidator + + Keys.onEnterPressed: btnCreate.onClicked() + Keys.onReturnPressed: btnCreate.onClicked() + } + } + + Text { + text: qsTr("Folder name cannot be empty.") + color: "#ff0000" + anchors.right: parent.right + visible: folderName.text === "" + } + + Item { // spacer + width: 1 + height: 20 + } + + Row { + anchors.right: parent.right + + Button { + id: btnCreate + + text: qsTr("Create") + enabled: folderName.text !== "" + onClicked: { + assetsModel.addNewFolder(root.contextDir.dirPath + '/' + folderName.text) + newFolderDialog.accept() + } + } + + Button { + text: qsTr("Cancel") + onClicked: newFolderDialog.reject() + } + } + } + + onOpened: { + folderName.text = qsTr("New folder") + folderName.selectAll() + folderName.forceActiveFocus() + } +} diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/RenameFolderDialog.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/RenameFolderDialog.qml new file mode 100644 index 00000000000..351c0a35fc8 --- /dev/null +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/RenameFolderDialog.qml @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuickDesignerTheme +import HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +Dialog { + id: renameFolderDialog + + title: qsTr("Rename Folder") + anchors.centerIn: parent + closePolicy: Popup.CloseOnEscape + implicitWidth: 280 + modal: true + + property bool renameError: false + + contentItem: Column { + spacing: 2 + + StudioControls.TextField { + id: folderRename + + actionIndicator.visible: false + translationIndicator.visible: false + width: renameFolderDialog.width - 12 + validator: folderNameValidator + + onEditChanged: renameFolderDialog.renameError = false + Keys.onEnterPressed: btnRename.onClicked() + Keys.onReturnPressed: btnRename.onClicked() + } + + Text { + text: qsTr("Folder name cannot be empty.") + color: "#ff0000" + visible: folderRename.text === "" && !renameFolderDialog.renameError + } + + Text { + text: qsTr("Could not rename folder. Make sure no folder with the same name exists.") + wrapMode: Text.WordWrap + width: renameFolderDialog.width - 12 + color: "#ff0000" + visible: renameFolderDialog.renameError + } + + Item { // spacer + width: 1 + height: 10 + } + + Text { + text: qsTr("If the folder has assets in use, renaming it might cause the project to not work correctly.") + color: StudioTheme.Values.themeTextColor + wrapMode: Text.WordWrap + width: renameFolderDialog.width + leftPadding: 10 + rightPadding: 10 + } + + Item { // spacer + width: 1 + height: 20 + } + + Row { + anchors.right: parent.right + + Button { + id: btnRename + + text: qsTr("Rename") + enabled: folderRename.text !== "" + onClicked: { + var success = assetsModel.renameFolder(root.contextDir.dirPath, folderRename.text) + if (success) + renameFolderDialog.accept() + + renameFolderDialog.renameError = !success + } + } + + Button { + text: qsTr("Cancel") + onClicked: renameFolderDialog.reject() + } + } + } + + onOpened: { + folderRename.text = root.contextDir.dirName + folderRename.selectAll() + folderRename.forceActiveFocus() + renameFolderDialog.renameError = false + } +} diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index 18967a5fe1d..c538b452729 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -57,6 +57,25 @@ static Q_LOGGING_CATEGORY(assetsLibraryBenchmark, "qtc.assetsLibrary.setRoot", Q namespace QmlDesigner { +AssetsLibraryModel::AssetsLibraryModel(Utils::FileSystemWatcher *fileSystemWatcher, QObject *parent) + : QAbstractListModel(parent) + , m_fileSystemWatcher(fileSystemWatcher) +{ + // add role names + int role = 0; + const QMetaObject meta = AssetsLibraryDir::staticMetaObject; + for (int i = meta.propertyOffset(); i < meta.propertyCount(); ++i) + m_roleNames.insert(role++, meta.property(i).name()); +} + +void AssetsLibraryModel::setSearchText(const QString &searchText) +{ + if (m_searchText != searchText) { + m_searchText = searchText; + refresh(); + } +} + void AssetsLibraryModel::saveExpandedState(bool expanded, const QString &assetPath) { m_expandedStateHash.insert(assetPath, expanded); @@ -208,66 +227,18 @@ QObject *AssetsLibraryModel::rootDir() const return m_assetsDir; } -const QStringList &AssetsLibraryModel::supportedImageSuffixes() +bool AssetsLibraryModel::isEmpty() const { - static QStringList retList; - if (retList.isEmpty()) { - const QList suffixes = QImageReader::supportedImageFormats(); - for (const QByteArray &suffix : suffixes) - retList.append("*." + QString::fromUtf8(suffix)); + return m_isEmpty; +}; + +void AssetsLibraryModel::setIsEmpty(bool empty) +{ + if (m_isEmpty != empty) { + m_isEmpty = empty; + emit isEmptyChanged(); } - return retList; -} - -const QStringList &AssetsLibraryModel::supportedFragmentShaderSuffixes() -{ - static const QStringList retList {"*.frag", "*.glsl", "*.glslf", "*.fsh"}; - return retList; -} - -const QStringList &AssetsLibraryModel::supportedShaderSuffixes() -{ - static const QStringList retList {"*.frag", "*.vert", - "*.glsl", "*.glslv", "*.glslf", - "*.vsh", "*.fsh"}; - return retList; -} - -const QStringList &AssetsLibraryModel::supportedFontSuffixes() -{ - static const QStringList retList {"*.ttf", "*.otf"}; - return retList; -} - -const QStringList &AssetsLibraryModel::supportedAudioSuffixes() -{ - static const QStringList retList {"*.wav", "*.mp3"}; - return retList; -} - -const QStringList &AssetsLibraryModel::supportedVideoSuffixes() -{ - static const QStringList retList {"*.mp4"}; - return retList; -} - -const QStringList &AssetsLibraryModel::supportedTexture3DSuffixes() -{ - // These are file types only supported by 3D textures - static QStringList retList {"*.hdr", "*.ktx"}; - return retList; -} - -AssetsLibraryModel::AssetsLibraryModel(Utils::FileSystemWatcher *fileSystemWatcher, QObject *parent) - : QAbstractListModel(parent) - , m_fileSystemWatcher(fileSystemWatcher) -{ - // add role names - int role = 0; - const QMetaObject meta = AssetsLibraryDir::staticMetaObject; - for (int i = meta.propertyOffset(); i < meta.propertyCount(); ++i) - m_roleNames.insert(role++, meta.property(i).name()); -} +}; QVariant AssetsLibraryModel::data(const QModelIndex &index, int role) const { @@ -377,12 +348,54 @@ void AssetsLibraryModel::setRootPath(const QString &path) qCInfo(assetsLibraryBenchmark) << "model reset:" << time.elapsed(); } -void AssetsLibraryModel::setSearchText(const QString &searchText) +const QStringList &AssetsLibraryModel::supportedImageSuffixes() { - if (m_searchText != searchText) { - m_searchText = searchText; - refresh(); + static QStringList retList; + if (retList.isEmpty()) { + const QList suffixes = QImageReader::supportedImageFormats(); + for (const QByteArray &suffix : suffixes) + retList.append("*." + QString::fromUtf8(suffix)); } + return retList; +} + +const QStringList &AssetsLibraryModel::supportedFragmentShaderSuffixes() +{ + static const QStringList retList {"*.frag", "*.glsl", "*.glslf", "*.fsh"}; + return retList; +} + +const QStringList &AssetsLibraryModel::supportedShaderSuffixes() +{ + static const QStringList retList {"*.frag", "*.vert", + "*.glsl", "*.glslv", "*.glslf", + "*.vsh", "*.fsh"}; + return retList; +} + +const QStringList &AssetsLibraryModel::supportedFontSuffixes() +{ + static const QStringList retList {"*.ttf", "*.otf"}; + return retList; +} + +const QStringList &AssetsLibraryModel::supportedAudioSuffixes() +{ + static const QStringList retList {"*.wav", "*.mp3"}; + return retList; +} + +const QStringList &AssetsLibraryModel::supportedVideoSuffixes() +{ + static const QStringList retList {"*.mp4"}; + return retList; +} + +const QStringList &AssetsLibraryModel::supportedTexture3DSuffixes() +{ + // These are file types only supported by 3D textures + static QStringList retList {"*.hdr", "*.ktx"}; + return retList; } const QSet &AssetsLibraryModel::supportedSuffixes() @@ -403,19 +416,6 @@ const QSet &AssetsLibraryModel::supportedSuffixes() return allSuffixes; } -bool AssetsLibraryModel::isEmpty() const -{ - return m_isEmpty; -}; - -void AssetsLibraryModel::setIsEmpty(bool empty) -{ - if (m_isEmpty != empty) { - m_isEmpty = empty; - emit isEmptyChanged(); - } -}; - const QSet &AssetsLibraryModel::previewableSuffixes() const { static QSet previewableSuffixes; From b0fa74756504e83b690ca737c6a67de1de8e32ba Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 10 Oct 2022 16:33:54 +0300 Subject: [PATCH 005/143] QmlDesigner: Add "imported" icon to the icons font Change-Id: I100a382a549b1945d7c7e83de0a3ffa609b5148c Reviewed-by: Miikka Heikkinen --- .../imports/StudioTheme/InternalConstants.qml | 242 +++++++++--------- .../imports/StudioTheme/icons.ttf | Bin 24432 -> 23968 bytes .../components/componentcore/theme.h | 4 +- 3 files changed, 121 insertions(+), 125 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml index 368a7e9b76f..484386ec946 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml @@ -71,128 +71,126 @@ QtObject { 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 infinity: "\u0073" - readonly property string keyframe: "\u0074" - readonly property string linkTriangle: "\u0075" - readonly property string linked: "\u0076" - readonly property string listView: "\u0077" - readonly property string lockOff: "\u0078" - readonly property string lockOn: "\u0079" - readonly property string materialPreviewEnvironment: "\u007A" - readonly property string materialPreviewModel: "\u007B" - readonly property string mergeCells: "\u007C" - readonly property string minus: "\u007D" - readonly property string mirror: "\u007E" - readonly property string newMaterial: "\u007F" - readonly property string openLink: "\u0080" - readonly property string openMaterialBrowser: "\u0081" - readonly property string orientation: "\u0082" - readonly property string paddingEdge: "\u0083" - readonly property string paddingFrame: "\u0084" - readonly property string pasteStyle: "\u0085" - readonly property string pause: "\u0086" - readonly property string pin: "\u0087" - readonly property string play: "\u0088" - readonly property string plus: "\u0089" - readonly property string promote: "\u008A" - readonly property string readOnly: "\u008B" - readonly property string redo: "\u008C" - readonly property string rotationFill: "\u008D" - readonly property string rotationOutline: "\u008E" - readonly property string search: "\u008F" - readonly property string sectionToggle: "\u0090" - readonly property string splitColumns: "\u0091" - readonly property string splitRows: "\u0092" - readonly property string startNode: "\u0093" - readonly property string testIcon: "\u0094" - readonly property string textAlignBottom: "\u0095" - readonly property string textAlignCenter: "\u0096" - readonly property string textAlignJustified: "\u0097" - readonly property string textAlignLeft: "\u0098" - readonly property string textAlignMiddle: "\u0099" - readonly property string textAlignRight: "\u009A" - readonly property string textAlignTop: "\u009B" - readonly property string textBulletList: "\u009D" - readonly property string textFullJustification: "\u009E" - readonly property string textNumberedList: "\u009F" - readonly property string tickIcon: "\u00A0" - readonly property string translationCreateFiles: "\u00A1" - readonly property string translationCreateReport: "\u00A2" - readonly property string translationExport: "\u00A3" - readonly property string translationImport: "\u00A4" - readonly property string translationSelectLanguages: "\u00A5" - readonly property string translationTest: "\u00A6" - readonly property string transparent: "\u00A7" - readonly property string triState: "\u00A8" - readonly property string triangleArcA: "\u00A9" - readonly property string triangleArcB: "\u00AA" - readonly property string triangleCornerA: "\u00AB" - readonly property string triangleCornerB: "\u00AC" - readonly property string unLinked: "\u00AE" - readonly property string undo: "\u00AF" - readonly property string unpin: "\u00B0" - readonly property string upDownIcon: "\u00B1" - readonly property string upDownSquare2: "\u00B2" - readonly property string visibilityOff: "\u00B3" - readonly property string visibilityOn: "\u00B4" - readonly property string wildcard: "\u00B5" - readonly property string wizardsAutomotive: "\u00B6" - readonly property string wizardsDesktop: "\u00B7" - readonly property string wizardsGeneric: "\u00B8" - readonly property string wizardsMcuEmpty: "\u00B9" - readonly property string wizardsMcuGraph: "\u00BA" - readonly property string wizardsMobile: "\u00BB" - readonly property string wizardsUnknown: "\u00BC" - readonly property string zoomAll: "\u00BD" - readonly property string zoomIn: "\u00BE" - readonly property string zoomOut: "\u00BF" - readonly property string zoomSelection: "\u00C0" + readonly property string colorPopupClose: "\u0045" + readonly property string columnsAndRows: "\u0046" + readonly property string copyStyle: "\u0047" + readonly property string cornerA: "\u0048" + readonly property string cornerB: "\u0049" + readonly property string cornersAll: "\u004A" + readonly property string curveDesigner: "\u004B" + readonly property string curveEditor: "\u004C" + readonly property string customMaterialEditor: "\u004D" + readonly property string decisionNode: "\u004E" + readonly property string deleteColumn: "\u004F" + readonly property string deleteMaterial: "\u0050" + readonly property string deleteRow: "\u0051" + readonly property string deleteTable: "\u0052" + readonly property string detach: "\u0053" + readonly property string distributeBottom: "\u0054" + readonly property string distributeCenterHorizontal: "\u0055" + readonly property string distributeCenterVertical: "\u0056" + readonly property string distributeLeft: "\u0057" + readonly property string distributeOriginBottomRight: "\u0058" + readonly property string distributeOriginCenter: "\u0059" + readonly property string distributeOriginNone: "\u005A" + readonly property string distributeOriginTopLeft: "\u005B" + readonly property string distributeRight: "\u005C" + readonly property string distributeSpacingHorizontal: "\u005D" + readonly property string distributeSpacingVertical: "\u005E" + readonly property string distributeTop: "\u005F" + readonly property string download: "\u0060" + readonly property string downloadUnavailable: "\u0061" + readonly property string downloadUpdate: "\u0062" + readonly property string downloaded: "\u0063" + readonly property string edit: "\u0064" + readonly property string eyeDropper: "\u0065" + readonly property string favorite: "\u0066" + readonly property string flowAction: "\u0067" + readonly property string flowTransition: "\u0068" + readonly property string fontStyleBold: "\u0069" + readonly property string fontStyleItalic: "\u006A" + readonly property string fontStyleStrikethrough: "\u006B" + readonly property string fontStyleUnderline: "\u006C" + readonly property string gradient: "\u006D" + readonly property string gridView: "\u006E" + readonly property string idAliasOff: "\u006F" + readonly property string idAliasOn: "\u0070" + readonly property string imported: "\u0071" + readonly property string infinity: "\u0072" + readonly property string keyframe: "\u0073" + readonly property string linkTriangle: "\u0074" + readonly property string linked: "\u0075" + readonly property string listView: "\u0076" + readonly property string lockOff: "\u0077" + readonly property string lockOn: "\u0078" + readonly property string materialPreviewEnvironment: "\u0079" + readonly property string materialPreviewModel: "\u007A" + readonly property string mergeCells: "\u007B" + readonly property string minus: "\u007C" + readonly property string mirror: "\u007D" + readonly property string newMaterial: "\u007E" + readonly property string openMaterialBrowser: "\u007F" + readonly property string orientation: "\u0080" + readonly property string paddingEdge: "\u0081" + readonly property string paddingFrame: "\u0082" + readonly property string pasteStyle: "\u0083" + readonly property string pause: "\u0084" + readonly property string pin: "\u0085" + readonly property string play: "\u0086" + readonly property string plus: "\u0087" + readonly property string promote: "\u0088" + readonly property string readOnly: "\u0089" + readonly property string redo: "\u008A" + readonly property string rotationFill: "\u008B" + readonly property string rotationOutline: "\u008C" + readonly property string search: "\u008D" + readonly property string sectionToggle: "\u008E" + readonly property string splitColumns: "\u008F" + readonly property string splitRows: "\u0090" + readonly property string startNode: "\u0091" + readonly property string testIcon: "\u0092" + readonly property string textAlignBottom: "\u0093" + readonly property string textAlignCenter: "\u0094" + readonly property string textAlignJustified: "\u0095" + readonly property string textAlignLeft: "\u0096" + readonly property string textAlignMiddle: "\u0097" + readonly property string textAlignRight: "\u0098" + readonly property string textAlignTop: "\u0099" + readonly property string textBulletList: "\u009A" + readonly property string textFullJustification: "\u009B" + readonly property string textNumberedList: "\u009D" + readonly property string tickIcon: "\u009E" + readonly property string translationCreateFiles: "\u009F" + readonly property string translationCreateReport: "\u00A0" + readonly property string translationExport: "\u00A1" + readonly property string translationImport: "\u00A2" + readonly property string translationSelectLanguages: "\u00A3" + readonly property string translationTest: "\u00A4" + readonly property string transparent: "\u00A5" + readonly property string triState: "\u00A6" + readonly property string triangleArcA: "\u00A7" + readonly property string triangleArcB: "\u00A8" + readonly property string triangleCornerA: "\u00A9" + readonly property string triangleCornerB: "\u00AA" + readonly property string unLinked: "\u00AB" + readonly property string undo: "\u00AC" + readonly property string unpin: "\u00AE" + readonly property string upDownIcon: "\u00AF" + readonly property string upDownSquare2: "\u00B0" + readonly property string visibilityOff: "\u00B1" + readonly property string visibilityOn: "\u00B2" + readonly property string wildcard: "\u00B3" + readonly property string wizardsAutomotive: "\u00B4" + readonly property string wizardsDesktop: "\u00B5" + readonly property string wizardsGeneric: "\u00B6" + readonly property string wizardsMcuEmpty: "\u00B7" + readonly property string wizardsMcuGraph: "\u00B8" + readonly property string wizardsMobile: "\u00B9" + readonly property string wizardsUnknown: "\u00BA" + readonly property string zoomAll: "\u00BB" + readonly property string zoomIn: "\u00BC" + readonly property string zoomOut: "\u00BD" + readonly property string zoomSelection: "\u00BE" readonly property font iconFont: Qt.font({ "family": controlIcons.name, diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf index acd8df6ce3ff87fe98d8d5df178d672e0d6d070e..e3b40281c8265d55a97d54aa1443743a3a6928af 100644 GIT binary patch delta 1450 zcmeyck8#0n#(D-u1_lORh6V;^h5$FW5Z}29ZJ7)Vu`LV?3^M+~`bLqhsVNK$j1deB z3<=4(i3RulcxEs#Ff%YPFa{-;l_)SMGF37#u!Jx$FtDU278m^g50Z?nVPIh3NYANE zlkq?Kf`Nf`1_Og9Pey8DifZhgas~#5I}8jAW*Hf&^@&`c|4(FKU=(3sU{J}(EvXRO z%Pqvfz_^2ffzd4|KRJ=5Ts((?fhB^0fzcs1v7&(CGs9^H21W}81_p(^#N5>B3yv@| zF!(KDU|_mgkY8L7#*xACfYXPwiE{_%FD?VFIIcZh7r0(+i>S`&*MJA zeS`Z8j{uJfPZiH0UOrww-f6r~_!#)q_$v5T@a^O0;5Xx+#D7A7L%>5IO<;$>1%YRR zI)Xid7lg!wI)v^Bs|ec&hY3#-{wHE5;w2I%(kHU0UgVX?4^cJI2+;>(5@H@=MPkdu zwun6v7Z8^dw-KKteosO}qD119q?lxm*Rw8WWfElF9K!vEjfstYvy;$7#>w^~C7b7qnsLOlFu48CWS+@%k%5hY zn?Z;{l7WFyosXH_kdIMKU09A;+?(RZxnHNY>-x?pE-pcKv)axZJ0o@fJl17n z=i=aGnyORR^Xx$-7ptTavu<7Qv-?%L%(A*{tc+Zen_1+dIaFRU>oCYNs4y_FD5sD7ZzhTHk({$Zy+Ng@^6pGH=b>Rf?N0)*Dx_A=`bp4C%+O9*dik{`INnmMs{{K zqp^8*wi#m@V?t+p^1ll?S=rf)=3@0xZU01K>O?c!K8n>xHEmXQun?%1X0QNfNd^W+ z21$kvD4U6aonaG{&CFoNr~zfOFi0{^hO${1_!-Ya*=!7wOo9x|42&$Cpi+bZR2+fW zj0{E$Ay9E920n&GP&P9|4#O@GTY-g>frY_{F&@NW;bdTCkYij6WwS9DG2Wg0D?n2# zmm!fMi6N08g`tQclYzlGzo4=xGd-h3K_gjH!N|bKNJk+zF)1;nD06a5-~moU1_K6N z215pe$;v?%tcC`jW7U8PQ?70Q`MJ1^zlf$D< hZPtrcWKjVXHUIyEiycrw%y7@=EvRHeDxuEI001)aT#Wz# delta 1998 zcmZ3moAJXw#(D-u1_lORh6V;^h5$FW5Z^hk*YYzk#D_32Fv$1^>l;OOq{c8XFh(#i zFeD`BCKlX(w|W)>12YQ)17lEfS&0IJB2y&;0}Du=B|WjY;QxOHW(J0M9|i^nj`W<$ zw2bd*pBNZe7cek*t;tADOi|SoI>^AlaEF0`!7L*qwLX#S<^PEc42&WS3=Aq6xg`~1 zh1?+i4h9BBublkkM6>^1Wef}~AV;|5CRP+Md}TPzz`$t1z`&r8mzbNn@5NhV28IA1 z1_q|51^LA#X^9`47#M<07#MgYz@B4dU^x40&*^x6o39Mq%r6)i7#QyRryqjQGU))^vJ|5-8|%5bgf|JF5#bPt6FDWyB`PMWRWBMPnkTwV^oZyu zF%_{jVo$_H#O=h3#2dudiQf@_CBY#PC$UQ6i=>0(G|5L&7E)8B9!S?nFOYsF<0aD} zb4=DrHb?fB>?b)MIW;*axeB>$a_{7u7ozgeu6y*~t1}a@D zXH?l#!x)$t6d4#8wU}lxY+lT|oRvw4WwQ_W8#X3(w#`;T6B+CE{--hTV*11&&A`CP zC?>+Dq|V96rle+K&dI1|VrC@H$!KOICc@67z&L?ZSd#N!!M_4dNnuXL3I7T}e8vfk z6To~O?Bf3lAo{?u&HUez`8l&Z12+Q$Clfm-qdF(!Yc4tpjT)yztAG{*S~Kd6POBpvW29C+>QSBgA4&1 z$hbLAyU?OwfkmcHrfQD&zkXp!&IT?J(U_r@VpTWC=@-y)M?vyF%)hdvX7+y8Rr znM@ZMm>Jj^7#P(V#f8}!&6)dk{yf&<(q)#_Wts|NGDqtEd92H@`HD<5hyM*`X$E-) zH3kM2B{g+(IYwh6GjlseHFZ5kB{p_-J4PchaXCg2Hg+~aQAJT*Mo}j3gWKG-ZRdrB z&$SJ~wcyJZx7xXHG2x0}})Le>-NP zEn_CmvcIa5f*6)J_VWj017b~biJHFaSzc4IS>$+v9{WVpEg z?GgFLvyGE;3m@YeCdMQkMn&!9R{{cC`1mHP+v(_<=9*+P8k?Ksnwl}DF(!1jC;z*U zlVy@?%4jZDAJz6xB&JR@v+bi;eN+pQSP7db`?-xwEeUKD7@$ig^}asK89L2*o6yvaHF#i>4-dD)Y#LbW-0 zlJg5HLA=e0p_xUCN2; Date: Tue, 27 Sep 2022 15:05:26 +0300 Subject: [PATCH 006/143] Fix copying dynamic properties on materials Fixes: QDS-7803 Change-Id: I24c8cd269965552a62fbbbc521efbff00811fa43 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Bot --- .../MaterialBrowser.qml | 5 +- .../materialbrowser/materialbrowsermodel.cpp | 60 ++++++++++++------- .../materialbrowser/materialbrowsermodel.h | 1 + .../materialbrowser/materialbrowserview.cpp | 38 +++++++++--- .../designercore/include/modelnode.h | 1 + .../designercore/model/modelnode.cpp | 12 ++++ 6 files changed, 88 insertions(+), 29 deletions(-) diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml index 1e50f3f99b2..2fecc7582ad 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml @@ -125,7 +125,10 @@ Item { width: parent.width onAboutToShow: { - root.matSectionsModel = ["All"]; + if (root.currentMaterial.hasDynamicProperties) + root.matSectionsModel = ["All", "Custom"]; + else + root.matSectionsModel = ["All"]; switch (root.currentMaterial.materialType) { case "DefaultMaterial": diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp index e9af23eb804..cb3a295259b 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp @@ -73,6 +73,9 @@ QVariant MaterialBrowserModel::data(const QModelIndex &index, int role) const return matType; } + if (roleName == "hasDynamicProperties") + return !m_materialList.at(index.row()).dynamicProperties().isEmpty(); + return {}; } @@ -143,7 +146,8 @@ QHash MaterialBrowserModel::roleNames() const {Qt::UserRole + 1, "materialName"}, {Qt::UserRole + 2, "materialInternalId"}, {Qt::UserRole + 3, "materialVisible"}, - {Qt::UserRole + 4, "materialType"} + {Qt::UserRole + 4, "materialType"}, + {Qt::UserRole + 5, "hasDynamicProperties"} }; return roles; } @@ -360,33 +364,47 @@ void MaterialBrowserModel::copyMaterialProperties(int idx, const QString §io setCopiedMaterialType(matType); m_allPropsCopied = section == "All"; + bool dynamicPropsCopied = section == "Custom"; QmlObjectNode mat(m_copiedMaterial); QSet validProps; + QHash dynamicProps; PropertyNameList copiedProps; - // Base state properties are always valid - const auto baseProps = m_copiedMaterial.propertyNames(); - for (const auto &baseProp : baseProps) - validProps.insert(baseProp); - - if (!mat.isInBaseState()) { - QmlPropertyChanges changes = mat.propertyChangeForCurrentState(); - if (changes.isValid()) { - const QList changedProps = changes.targetProperties(); - for (const auto &changedProp : changedProps) - validProps.insert(changedProp.name()); + if (dynamicPropsCopied || m_allPropsCopied) { + // Dynamic properties must always be set in base state + const QList dynProps = m_copiedMaterial.dynamicProperties(); + for (const auto &prop : dynProps) { + dynamicProps.insert(prop.name(), prop.dynamicTypeName()); + validProps.insert(prop.name()); } } - if (mat.timelineIsActive()) { - const QList keyframeGroups - = mat.currentTimeline().keyframeGroupsForTarget(m_copiedMaterial); - for (const auto &kfg : keyframeGroups) - validProps.insert(kfg.propertyName()); - } + if (!dynamicPropsCopied) { + // Base state properties are always valid + const auto baseProps = m_copiedMaterial.propertyNames(); + for (const auto &baseProp : baseProps) + validProps.insert(baseProp); - if (m_allPropsCopied || m_propertyGroupsObj.empty()) { + if (!mat.isInBaseState()) { + QmlPropertyChanges changes = mat.propertyChangeForCurrentState(); + if (changes.isValid()) { + const QList changedProps = changes.targetProperties(); + for (const auto &changedProp : changedProps) + validProps.insert(changedProp.name()); + } + } + + if (mat.timelineIsActive()) { + const QList keyframeGroups + = mat.currentTimeline().keyframeGroupsForTarget(m_copiedMaterial); + for (const auto &kfg : keyframeGroups) + validProps.insert(kfg.propertyName()); + } + } + validProps.remove("objectName"); + + if (m_allPropsCopied || dynamicPropsCopied || m_propertyGroupsObj.empty()) { copiedProps = validProps.values(); } else { QJsonObject propsSpecObj = m_propertyGroupsObj.value(m_copiedMaterialType).toObject(); @@ -411,8 +429,10 @@ void MaterialBrowserModel::copyMaterialProperties(int idx, const QString §io PropertyCopyData data; data.name = propName; data.isValid = m_allPropsCopied || validProps.contains(propName); - data.isBinding = mat.hasBindingProperty(propName); if (data.isValid) { + if (dynamicProps.contains(propName)) + data.dynamicTypeName = dynamicProps[propName]; + data.isBinding = mat.hasBindingProperty(propName); if (data.isBinding) data.value = mat.expression(propName); else diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h index c054d07527b..3cd8b65a411 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h @@ -97,6 +97,7 @@ public: struct PropertyCopyData { PropertyName name; + TypeName dynamicTypeName; QVariant value; bool isBinding = false; bool isValid = false; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 01ee657f848..94c75d93a77 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -25,6 +25,7 @@ #include "materialbrowserview.h" +#include "bindingproperty.h" #include "bundlematerial.h" #include "materialbrowserwidget.h" #include "materialbrowsermodel.h" @@ -105,13 +106,19 @@ WidgetInfo MaterialBrowserView::widgetInfo() // remove current properties PropertyNameList propNames; if (mat.isInBaseState()) { - propNames = material.propertyNames(); + const QList baseProps = material.properties(); + for (const auto &baseProp : baseProps) { + if (!baseProp.isDynamic()) + propNames.append(baseProp.name()); + } } else { QmlPropertyChanges changes = mat.propertyChangeForCurrentState(); if (changes.isValid()) { const QList changedProps = changes.targetProperties(); - for (const auto &changedProp : changedProps) - propNames.append(changedProp.name()); + for (const auto &changedProp : changedProps) { + if (!changedProp.isDynamic()) + propNames.append(changedProp.name()); + } } } for (const PropertyName &propName : qAsConst(propNames)) { @@ -122,14 +129,29 @@ WidgetInfo MaterialBrowserView::widgetInfo() // apply pasted properties for (const QmlDesigner::MaterialBrowserModel::PropertyCopyData &propData : propDatas) { - if (propData.name == "objectName") - continue; - if (propData.isValid) { - if (propData.isBinding) + const bool isDynamic = !propData.dynamicTypeName.isEmpty(); + const bool isBaseState = currentState().isBaseState(); + const bool hasProperty = mat.hasProperty(propData.name); + if (propData.isBinding) { + if (isDynamic && (!hasProperty || isBaseState)) { + mat.modelNode().bindingProperty(propData.name) + .setDynamicTypeNameAndExpression( + propData.dynamicTypeName, propData.value.toString()); + continue; + } mat.setBindingProperty(propData.name, propData.value.toString()); - else + } else { + const bool isRecording = mat.timelineIsActive() + && mat.currentTimeline().isRecording(); + if (isDynamic && (!hasProperty || (isBaseState && !isRecording))) { + mat.modelNode().variantProperty(propData.name) + .setDynamicTypeNameAndValue( + propData.dynamicTypeName, propData.value); + continue; + } mat.setVariantProperty(propData.name, propData.value); + } } else { mat.removeProperty(propData.name); } diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index def083f02f6..e44234fffb6 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -145,6 +145,7 @@ public: QList nodeListProperties() const; QList bindingProperties() const; QList signalProperties() const; + QList dynamicProperties() const; PropertyNameList propertyNames() const; bool hasProperties() const; diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index 913c67b1a24..c221215acc0 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -691,6 +691,18 @@ QList ModelNode::signalProperties() const return propertyList; } +QList ModelNode::dynamicProperties() const +{ + QList propertyList; + + const QList abstractProperties = properties(); + for (const AbstractProperty &abstractProperty : abstractProperties) { + if (abstractProperty.isDynamic()) + propertyList.append(abstractProperty); + } + return propertyList; +} + /*! \brief removes a property from this node \param name name of the property From 007a9a46a52b8a71b8c78ff50456033b720362a5 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 10 Oct 2022 17:41:40 +0300 Subject: [PATCH 007/143] QmlDesigner: Reset item library drag on model detach Fixes: QDS-7823 Change-Id: If0b758f79dd4a55356d2cd5aefe65fa3e6e321be Reviewed-by: Reviewed-by: Mahmoud Badri --- .../qmldesigner/components/itemlibrary/itemlibrarywidget.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 51b807d614a..a72c334a4cb 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -295,8 +295,10 @@ void ItemLibraryWidget::delayedUpdateModel() void ItemLibraryWidget::setModel(Model *model) { m_model = model; - if (!model) + if (!model) { + m_itemToDrag = {}; return; + } setItemLibraryInfo(model->metaInfo().itemLibraryInfo()); From e18bd4b0f87231db09a6fb1f4c7d61a1e4c14b4e Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Wed, 12 Oct 2022 10:34:34 +0200 Subject: [PATCH 008/143] FSEngine: Invalidate cache after creating a file Since QFile::open can change the "exists" info of a file, we have to invalidate the cache entry for the file. Otherwise code like: QFile f(...) f.open(QIODevice::WriteOnly); f.exists(); might fail the exists check even though the file was created Change-Id: If80eaf4a5b131cfe7ea4e506292870741c46fff7 Reviewed-by: hjk --- src/libs/utils/fsengine/filepathinfocache.h | 6 ++++++ src/libs/utils/fsengine/fsengine_impl.cpp | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/libs/utils/fsengine/filepathinfocache.h b/src/libs/utils/fsengine/filepathinfocache.h index e0b7094cbfe..8b88234e3dc 100644 --- a/src/libs/utils/fsengine/filepathinfocache.h +++ b/src/libs/utils/fsengine/filepathinfocache.h @@ -60,6 +60,12 @@ public: m_cache.insert(path, new CachedData(data)); } + void invalidate(const FilePath &path) + { + QMutexLocker lk(&m_mutex); + m_cache.remove(path); + } + private: QMutex m_mutex; QCache m_cache; diff --git a/src/libs/utils/fsengine/fsengine_impl.cpp b/src/libs/utils/fsengine/fsengine_impl.cpp index f8277195b28..e09bc94609e 100644 --- a/src/libs/utils/fsengine/fsengine_impl.cpp +++ b/src/libs/utils/fsengine/fsengine_impl.cpp @@ -44,6 +44,8 @@ bool FSEngineImpl::open(QIODevice::OpenMode openMode) createCacheData); bool exists = (data.filePathInfo.fileFlags & QAbstractFileEngine::ExistsFlag); + g_filePathInfoCache.invalidate(m_filePath); + ensureStorage(); QTC_ASSERT(m_tempStorage->open(), return false); From 48755cf7fd88790be879b31bf7fbbf66b9238c98 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 11 Oct 2022 17:32:38 +0200 Subject: [PATCH 009/143] Revert "ClangCodeModel: Rename via LSP facilities" We cannot use clangd's rename facilities yet, as there is a hardcoded limit of affected files. This mostly reverts commit 7dc2c6b3b32f433cb0b0e26e2559079782f99efb. Change-Id: Ie441796569b533948cc028c867175d6f9d4b9d54 Reviewed-by: Reviewed-by: David Schulz --- src/plugins/clangcodemodel/clangdclient.cpp | 26 +++--- .../clangcodemodel/clangdfindreferences.cpp | 93 ++++++++++++++++++- .../clangcodemodel/clangdfindreferences.h | 2 +- 3 files changed, 106 insertions(+), 15 deletions(-) diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index e43ca885c50..5c249fa2290 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -260,7 +260,7 @@ public: : q(q), settings(CppEditor::ClangdProjectSettings(project).settings()) {} void findUsages(TextDocument *document, const QTextCursor &cursor, - const QString &searchTerm, + const QString &searchTerm, const std::optional &replacement, bool categorize); void handleDeclDefSwitchReplies(); @@ -462,11 +462,13 @@ void ClangdClient::findUsages(TextDocument *document, const QTextCursor &cursor, if (searchTerm.isEmpty()) return; - if (replacement) { - symbolSupport().renameSymbol(document, adjustedCursor, *replacement, - CppEditor::preferLowerCaseFileNames()); - return; - } + // TODO: Fix hard file limit in clangd, then uncomment this with version check. + // Will fix QTCREATORBUG-27978 and QTCREATORBUG-28109. + // if (replacement) { + // symbolSupport().renameSymbol(document, adjustedCursor, *replacement, + // CppEditor::preferLowerCaseFileNames()); + // return; + // } const bool categorize = CppEditor::codeModelSettings()->categorizeFindReferences(); @@ -474,19 +476,19 @@ void ClangdClient::findUsages(TextDocument *document, const QTextCursor &cursor, if (searchTerm != "operator" && Utils::allOf(searchTerm, [](const QChar &c) { return c.isLetterOrNumber() || c == '_'; })) { - d->findUsages(document, adjustedCursor, searchTerm, categorize); + d->findUsages(document, adjustedCursor, searchTerm, replacement, categorize); return; } // Otherwise get the proper spelling of the search term from clang, so we can put it into the // search widget. - const auto symbolInfoHandler = [this, doc = QPointer(document), adjustedCursor, categorize] + const auto symbolInfoHandler = [this, doc = QPointer(document), adjustedCursor, replacement, categorize] (const QString &name, const QString &, const MessageId &) { if (!doc) return; if (name.isEmpty()) return; - d->findUsages(doc.data(), adjustedCursor, name, categorize); + d->findUsages(doc.data(), adjustedCursor, name, replacement, categorize); }; requestSymbolInfo(document->filePath(), Range(adjustedCursor).start(), symbolInfoHandler); } @@ -642,9 +644,11 @@ QVersionNumber ClangdClient::versionNumber() const CppEditor::ClangdSettings::Data ClangdClient::settingsData() const { return d->settings; } void ClangdClient::Private::findUsages(TextDocument *document, - const QTextCursor &cursor, const QString &searchTerm, bool categorize) + const QTextCursor &cursor, const QString &searchTerm, + const std::optional &replacement, bool categorize) { - const auto findRefs = new ClangdFindReferences(q, document, cursor, searchTerm, categorize); + const auto findRefs = new ClangdFindReferences(q, document, cursor, searchTerm, replacement, + categorize); if (isTesting) { connect(findRefs, &ClangdFindReferences::foundReferences, q, &ClangdClient::foundReferences); diff --git a/src/plugins/clangcodemodel/clangdfindreferences.cpp b/src/plugins/clangcodemodel/clangdfindreferences.cpp index ce0abac572e..f5dcaf71e19 100644 --- a/src/plugins/clangcodemodel/clangdfindreferences.cpp +++ b/src/plugins/clangcodemodel/clangdfindreferences.cpp @@ -44,12 +44,25 @@ public: ClangdAstNode ast; }; +class ReplacementData { +public: + QString oldSymbolName; + QString newSymbolName; + QSet fileRenameCandidates; +}; + class ClangdFindReferences::Private { public: Private(ClangdFindReferences *q) : q(q) {} ClangdClient *client() const { return qobject_cast(q->parent()); } + static void handleRenameRequest( + const SearchResult *search, + const ReplacementData &replacementData, + const QString &newSymbolName, + const QList &checkedItems, + bool preserveCase); void handleFindUsagesResult(const QList &locations); void finishSearch(); void reportAllSearchResultsAndFinish(); @@ -61,24 +74,50 @@ public: QMap fileData; QList pendingAstRequests; QPointer search; + std::optional replacementData; bool canceled = false; bool categorize = false; }; ClangdFindReferences::ClangdFindReferences(ClangdClient *client, TextDocument *document, - const QTextCursor &cursor, const QString &searchTerm, bool categorize) + const QTextCursor &cursor, const QString &searchTerm, + const std::optional &replacement, bool categorize) : QObject(client), d(new ClangdFindReferences::Private(this)) { d->categorize = categorize; + if (replacement) { + ReplacementData replacementData; + replacementData.oldSymbolName = searchTerm; + replacementData.newSymbolName = *replacement; + if (replacementData.newSymbolName.isEmpty()) + replacementData.newSymbolName = replacementData.oldSymbolName; + d->replacementData = replacementData; + } + d->search = SearchResultWindow::instance()->startNewSearch( tr("C++ Usages:"), {}, searchTerm, - SearchResultWindow::SearchOnly, + replacement ? SearchResultWindow::SearchAndReplace : SearchResultWindow::SearchOnly, SearchResultWindow::PreserveCaseDisabled, "CppEditor"); if (categorize) d->search->setFilter(new CppSearchResultFilter); + if (d->replacementData) { + d->search->setTextToReplace(d->replacementData->newSymbolName); + const auto renameFilesCheckBox = new QCheckBox; + renameFilesCheckBox->setVisible(false); + d->search->setAdditionalReplaceWidget(renameFilesCheckBox); + const auto renameHandler = + [search = d->search](const QString &newSymbolName, + const QList &checkedItems, + bool preserveCase) { + const auto replacementData = search->userData().value(); + Private::handleRenameRequest(search, replacementData, newSymbolName, checkedItems, + preserveCase); + }; + connect(d->search, &SearchResult::replaceButtonClicked, renameHandler); + } connect(d->search, &SearchResult::activated, [](const SearchResultItem& item) { EditorManager::openEditorAtSearchResult(item); }); @@ -112,6 +151,31 @@ ClangdFindReferences::~ClangdFindReferences() delete d; } +void ClangdFindReferences::Private::handleRenameRequest( + const SearchResult *search, + const ReplacementData &replacementData, + const QString &newSymbolName, + const QList &checkedItems, + bool preserveCase) +{ + const Utils::FilePaths filePaths = BaseFileFind::replaceAll(newSymbolName, checkedItems, + preserveCase); + if (!filePaths.isEmpty()) { + DocumentManager::notifyFilesChangedInternally(filePaths); + SearchResultWindow::instance()->hide(); + } + + const auto renameFilesCheckBox = qobject_cast(search->additionalReplaceWidget()); + QTC_ASSERT(renameFilesCheckBox, return); + if (!renameFilesCheckBox->isChecked()) + return; + + ProjectExplorerPlugin::renameFilesForSymbol( + replacementData.oldSymbolName, newSymbolName, + Utils::toList(replacementData.fileRenameCandidates), + CppEditor::preferLowerCaseFileNames()); +} + void ClangdFindReferences::Private::handleFindUsagesResult(const QList &locations) { if (!search || canceled) { @@ -154,7 +218,7 @@ void ClangdFindReferences::Private::handleFindUsagesResult(const QList } qCDebug(clangdLog) << "document count is" << fileData.size(); - if (!categorize) { + if (replacementData || !categorize) { qCDebug(clangdLog) << "skipping AST retrieval"; reportAllSearchResultsAndFinish(); return; @@ -198,6 +262,18 @@ void ClangdFindReferences::Private::finishSearch() if (!client()->testingEnabled() && search) { search->finishSearch(canceled); search->disconnect(q); + if (replacementData) { + const auto renameCheckBox = qobject_cast( + search->additionalReplaceWidget()); + QTC_CHECK(renameCheckBox); + const QSet files = replacementData->fileRenameCandidates; + renameCheckBox->setText(tr("Re&name %n files", nullptr, files.size())); + const QStringList filesForUser = Utils::transform(files, + [](const Utils::FilePath &fp) { return fp.toUserOutput(); }); + renameCheckBox->setToolTip(tr("Files:\n%1").arg(filesForUser.join('\n'))); + renameCheckBox->setVisible(true); + search->setUserData(QVariant::fromValue(*replacementData)); + } } emit q->done(); q->deleteLater(); @@ -231,6 +307,15 @@ void ClangdFindReferences::Private::addSearchResultsForFile(const FilePath &file item.setUseTextEditorFont(true); item.setLineText(rangeWithText.second); item.setContainingFunctionName(getContainingFunctionName(astPath, range)); + + if (search->supportsReplace()) { + const bool fileInSession = SessionManager::projectForFile(file); + item.setSelectForReplacement(fileInSession); + if (fileInSession && file.baseName().compare(replacementData->oldSymbolName, + Qt::CaseInsensitive) == 0) { + replacementData->fileRenameCandidates << file; + } + } items << item; } if (client()->testingEnabled()) @@ -484,3 +569,5 @@ void ClangdFindLocalReferences::Private::finish() } } // namespace ClangCodeModel::Internal + +Q_DECLARE_METATYPE(ClangCodeModel::Internal::ReplacementData) diff --git a/src/plugins/clangcodemodel/clangdfindreferences.h b/src/plugins/clangcodemodel/clangdfindreferences.h index 27d25a293ca..f45a61b25e4 100644 --- a/src/plugins/clangcodemodel/clangdfindreferences.h +++ b/src/plugins/clangcodemodel/clangdfindreferences.h @@ -25,7 +25,7 @@ class ClangdFindReferences : public QObject public: explicit ClangdFindReferences(ClangdClient *client, TextEditor::TextDocument *document, const QTextCursor &cursor, const QString &searchTerm, - bool categorize); + const std::optional &replacement, bool categorize); ~ClangdFindReferences(); signals: From b1471fe61fbf64c78fb7eef1a52f0b913541c85c Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 11 Oct 2022 13:58:26 +0200 Subject: [PATCH 010/143] Utils: Combine some of the iterateWithFind overloads Change-Id: I668447d0049f2e35cd0c76d2c5708779196167ec Reviewed-by: hjk Reviewed-by: Marcus Tillmanns --- src/libs/utils/filepath.cpp | 23 ++--- src/libs/utils/filepath.h | 25 +++-- src/libs/utils/fileutils.cpp | 94 +++++++------------ src/libs/utils/fileutils.h | 13 --- src/plugins/docker/dockerdevice.cpp | 13 +-- src/plugins/docker/dockerdevice.h | 10 +- .../devicesupport/desktopdevice.cpp | 7 +- .../devicesupport/desktopdevice.h | 7 +- .../devicesupport/devicemanager.cpp | 8 -- .../projectexplorer/devicesupport/idevice.cpp | 9 -- .../projectexplorer/devicesupport/idevice.h | 11 +-- src/plugins/remotelinux/linuxdevice.cpp | 9 -- src/plugins/remotelinux/linuxdevice.h | 3 - tests/auto/utils/fsengine/tst_fsengine.cpp | 20 ++-- 14 files changed, 83 insertions(+), 169 deletions(-) diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 73b414cbac5..22d4d9b42cc 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -521,26 +521,15 @@ void FilePath::iterateDirectory(const IterateDirCallback &callBack, const FileFi return; } - QDirIterator it(path(), filter.nameFilters, filter.fileFilters, filter.iteratorFlags); - while (it.hasNext()) { - if (!callBack(FilePath::fromString(it.next()))) - return; - } -} - -void FilePath::iterateDirectory(const IterateDirWithInfoCallback &callBack, - const FileFilter &filter) const -{ - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.iterateDirectoryWithInfo, return); - s_deviceHooks.iterateDirectoryWithInfo(*this, callBack, filter); - return; - } - QDirIterator it(path(), filter.nameFilters, filter.fileFilters, filter.iteratorFlags); while (it.hasNext()) { const FilePath path = FilePath::fromString(it.next()); - if (!callBack(path, path.filePathInfo())) + bool res = false; + if (callBack.index() == 0) + res = std::get<0>(callBack)(path); + else + res = std::get<1>(callBack)(path, path.filePathInfo()); + if (!res) return; } } diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h index f03d89d4d8b..055e0f135fb 100644 --- a/src/libs/utils/filepath.h +++ b/src/libs/utils/filepath.h @@ -154,17 +154,20 @@ public: [[nodiscard]] FilePath onDevice(const FilePath &deviceTemplate) const; [[nodiscard]] FilePath withNewPath(const QString &newPath) const; - using IterateDirCallback = std::function; - using IterateDirWithInfoCallback - = std::function; + using IterateDirCallback + = std::variant< + std::function, + std::function + >; - void iterateDirectory(const IterateDirCallback &callBack, const FileFilter &filter) const; - void iterateDirectory(const IterateDirWithInfoCallback &callBack, - const FileFilter &filter) const; + void iterateDirectory( + const IterateDirCallback &callBack, + const FileFilter &filter) const; - static void iterateDirectories(const FilePaths &dirs, - const IterateDirCallback &callBack, - const FileFilter &filter); + static void iterateDirectories( + const FilePaths &dirs, + const IterateDirCallback &callBack, + const FileFilter &filter); enum PathAmending { AppendToPath, PrependToPath }; [[nodiscard]] FilePath searchInPath(const FilePaths &additionalDirs = {}, @@ -270,10 +273,6 @@ public: const FilePath::IterateDirCallback &, // Abort on 'false' return. const FileFilter &)> iterateDirectory; - std::function - iterateDirectoryWithInfo; std::function(const FilePath &, qint64, qint64)> fileContents; std::function writeFileContents; std::function lastModified; diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index cb893ff4a46..f5ccc9db43c 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -587,10 +587,10 @@ FilePath FileUtils::getOpenFilePathFromDevice(QWidget *parent, #endif // QT_WIDGETS_LIB // Used on 'ls' output on unix-like systems. -void FileUtils::iterateLsOutput(const FilePath &base, - const QStringList &entries, - const FileFilter &filter, - const std::function &callBack) +static void iterateLsOutput(const FilePath &base, + const QStringList &entries, + const FileFilter &filter, + const FilePath::IterateDirCallback &callBack) { const QList nameRegexps = transform(filter.nameFilters, [](const QString &filter) { @@ -615,7 +615,13 @@ void FileUtils::iterateLsOutput(const FilePath &base, for (const QString &entry : entries) { if (!nameMatches(entry)) continue; - if (!callBack(base.pathAppended(entry))) + const FilePath current = base.pathAppended(entry); + bool res = false; + if (callBack.index() == 0) + res = std::get<0>(callBack)(current); + else + res = std::get<1>(callBack)(current, current.filePathInfo()); + if (!res) break; } } @@ -675,11 +681,12 @@ FilePathInfo FileUtils::filePathInfoFromTriple(const QString &infos) return {size, flags, dt}; } -bool iterateWithFind(const FilePath &filePath, - const FileFilter &filter, - const std::function &runInShell, - const std::function callBack, - const QString &extraArguments) +static bool iterateWithFindHelper( + const FilePath &filePath, + const FileFilter &filter, + const std::function &runInShell, + const std::function callBack, + const QString &extraArguments) { QTC_CHECK(filePath.isAbsolutePath()); const QStringList arguments = filter.asFindArguments(filePath.path()); @@ -716,29 +723,16 @@ bool iterateWithFind(const FilePath &filePath, } // returns whether 'find' could be used. -static bool iterateWithFind(const FilePath &filePath, - const FileFilter &filter, - const std::function &runInShell, - const FilePath::IterateDirCallback &callBack) +static bool iterateWithFind( + const FilePath &filePath, + const FileFilter &filter, + const std::function &runInShell, + const FilePath::IterateDirCallback &callBack) { - const auto toFilePath = [&filePath, &callBack](const QString &entry){ - return callBack(filePath.withNewPath(entry)); - }; + const auto toFilePath = [&filePath, &callBack](const QString &entry) { + if (callBack.index() == 0) + return std::get<0>(callBack)(filePath.withNewPath(entry)); - return iterateWithFind(filePath, filter, runInShell, toFilePath, {}); -} - -// returns whether 'find' could be used. -static bool iterateWithFind(const FilePath &filePath, - const FileFilter &filter, - const std::function &runInShell, - const FilePath::IterateDirWithInfoCallback &callBack) -{ - // 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 infoArgs(R"(-exec echo -n \"{}\"" " \; -exec stat -L -c "%f %Y %s" "{}" \;)"); - - const auto toFilePathAndInfo = [&filePath, &callBack](const QString &entry) { const QString fileName = entry.mid(1, entry.lastIndexOf('\"') - 1); const QString infos = entry.mid(fileName.length() + 3); @@ -747,10 +741,16 @@ static bool iterateWithFind(const FilePath &filePath, return true; const FilePath fp = filePath.withNewPath(fileName); - return callBack(fp, fi); + return std::get<1>(callBack)(fp, fi); }; - return iterateWithFind(filePath, filter, runInShell, toFilePathAndInfo, infoArgs); + // 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. + QString infoArgs; + if (callBack.index() == 1) + infoArgs = R"(-exec echo -n \"{}\"" " \; -exec stat -L -c "%f %Y %s" "{}" \;)"; + + return iterateWithFindHelper(filePath, filter, runInShell, toFilePath, infoArgs); } static void findUsingLs(const QString ¤t, @@ -777,8 +777,6 @@ void FileUtils::iterateUnixDirectory(const FilePath &filePath, const std::function &runInShell, const FilePath::IterateDirCallback &callBack) { - QTC_ASSERT(callBack, return); - // We try to use 'find' first, because that can filter better directly. // Unfortunately, it's not installed on all devices by default. if (useFind && *useFind) { @@ -790,31 +788,7 @@ void FileUtils::iterateUnixDirectory(const FilePath &filePath, // if we do not have find - use ls as fallback QStringList entries; findUsingLs(filePath.path(), filter, runInShell, &entries); - FileUtils::iterateLsOutput(filePath, entries, filter, callBack); -} - -void FileUtils::iterateUnixDirectory(const FilePath &filePath, - const FileFilter &filter, - bool *useFind, - const std::function &runInShell, - const FilePath::IterateDirWithInfoCallback &callBack) -{ - QTC_ASSERT(callBack, return); - - // We try to use 'find' first, because that can filter better directly. - // Unfortunately, it's not installed on all devices by default. - if (useFind && *useFind) { - if (iterateWithFind(filePath, filter, runInShell, callBack)) - return; - *useFind = false; // remember the failure for the next time and use the 'ls' fallback below. - } - - // if we do not have find - use ls as fallback - QStringList entries; - findUsingLs(filePath.path(), filter, runInShell, &entries); - FileUtils::iterateLsOutput(filePath, entries, filter, [&callBack](const FilePath & filePath){ - return callBack(filePath, filePath.filePathInfo()); - }); + iterateLsOutput(filePath, entries, filter, callBack); } /*! diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index fe3a388bb0b..5f6452b3650 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -86,12 +86,6 @@ public: static FilePaths toFilePathList(const QStringList &paths); - static void iterateLsOutput( - const FilePath &base, - const QStringList &entries, - const FileFilter &filter, - const std::function &callBack); - static void iterateUnixDirectory( const FilePath &base, const FileFilter &filter, @@ -99,13 +93,6 @@ public: const std::function &runInShell, const FilePath::IterateDirCallback &callBack); - static void iterateUnixDirectory( - const FilePath &base, - const FileFilter &filter, - bool *useFind, - const std::function &runInShell, - const FilePath::IterateDirWithInfoCallback &callBack); - static qint64 bytesAvailableFromDFOutput(const QByteArray &dfOutput); static FilePathInfo filePathInfoFromTriple(const QString &infos); diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 87815a7b3fd..bcab94bcb8b 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -948,17 +948,8 @@ bool DockerDevice::ensureReachable(const FilePath &other) const } void DockerDevice::iterateDirectory(const FilePath &filePath, - const FilePath::IterateDirCallback &callBack, - const FileFilter &filter) const -{ - QTC_ASSERT(handlesFile(filePath), return); - auto runInShell = [this](const CommandLine &cmd) { return d->runInShell(cmd); }; - FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack); -} - -void DockerDevice::iterateDirectory(const FilePath &filePath, - const FilePath::IterateDirWithInfoCallback &callBack, - const FileFilter &filter) const + const FilePath::IterateDirCallback &callBack, + const FileFilter &filter) const { QTC_ASSERT(handlesFile(filePath), return); auto runInShell = [this](const CommandLine &cmd) { return d->runInShell(cmd); }; diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h index 8398ee4e235..1382dc7d7b6 100644 --- a/src/plugins/docker/dockerdevice.h +++ b/src/plugins/docker/dockerdevice.h @@ -99,12 +99,10 @@ public: bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override; - void iterateDirectory(const Utils::FilePath &filePath, - const Utils::FilePath::IterateDirCallback &callBack, - const Utils::FileFilter &filter) const override; - void iterateDirectory(const Utils::FilePath &filePath, - const Utils::FilePath::IterateDirWithInfoCallback &callBack, - const Utils::FileFilter &filter) const override; + void iterateDirectory( + const Utils::FilePath &filePath, + const Utils::FilePath::IterateDirCallback &callBack, + const Utils::FileFilter &filter) const override; std::optional fileContents(const Utils::FilePath &filePath, qint64 limit, qint64 offset) const override; diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp index 105e80e34f1..ee5dae12e09 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp @@ -136,9 +136,10 @@ bool DesktopDevice::handlesFile(const FilePath &filePath) const return !filePath.needsDevice(); } -void DesktopDevice::iterateDirectory(const FilePath &filePath, - const std::function &callBack, - const FileFilter &filter) const +void DesktopDevice::iterateDirectory( + const FilePath &filePath, + const FilePath::IterateDirCallback &callBack, + const FileFilter &filter) const { QTC_CHECK(!filePath.needsDevice()); filePath.iterateDirectory(callBack, filter); diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.h b/src/plugins/projectexplorer/devicesupport/desktopdevice.h index a2a17237ac3..d0d89f7458c 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.h +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.h @@ -50,9 +50,10 @@ public: bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; QDateTime lastModified(const Utils::FilePath &filePath) const override; Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override; - void iterateDirectory(const Utils::FilePath &filePath, - const std::function &callBack, - const Utils::FileFilter &filter) const override; + void iterateDirectory( + const Utils::FilePath &filePath, + const Utils::FilePath::IterateDirCallback &callBack, + const Utils::FileFilter &filter) const override; std::optional fileContents(const Utils::FilePath &filePath, qint64 limit, qint64 offset) const override; diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index c5a7f4e8102..8a98a11b292 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -528,14 +528,6 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_uniqueiterateDirectory(filePath, callBack, filter); }; - deviceHooks.iterateDirectoryWithInfo = [](const FilePath &filePath, - const FilePath::IterateDirWithInfoCallback &callBack, - const FileFilter &filter) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return ); - device->iterateDirectory(filePath, callBack, filter); - }; - deviceHooks.fileContents = [](const FilePath &filePath, qint64 maxSize, qint64 offset) -> std::optional { auto device = DeviceManager::deviceForPath(filePath); diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index 294be9f18a0..ed6dcc73824 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -365,15 +365,6 @@ void IDevice::iterateDirectory(const FilePath &filePath, QTC_CHECK(false); } -void IDevice::iterateDirectory(const FilePath &filePath, - const FilePath::IterateDirWithInfoCallback &callBack, - const FileFilter &filter) const -{ - iterateDirectory(filePath, [callBack](const FilePath &path) { - return callBack(path, path.filePathInfo()); - }, filter); -} - std::optional IDevice::fileContents(const FilePath &filePath, qint64 limit, qint64 offset) const diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h index 57f2ba0a3c7..ac46ff77f47 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.h +++ b/src/plugins/projectexplorer/devicesupport/idevice.h @@ -237,13 +237,10 @@ public: virtual Utils::FilePath searchExecutable(const QString &fileName, const Utils::FilePaths &dirs) const; virtual Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const; - virtual void iterateDirectory(const Utils::FilePath &filePath, - const Utils::FilePath::IterateDirCallback &callBack, - const Utils::FileFilter &filter) const; - - virtual void iterateDirectory(const Utils::FilePath &filePath, - const Utils::FilePath::IterateDirWithInfoCallback &callBack, - const Utils::FileFilter &filter) const; + virtual void iterateDirectory( + const Utils::FilePath &filePath, + const Utils::FilePath::IterateDirCallback &callBack, + const Utils::FileFilter &filter) const; virtual std::optional fileContents(const Utils::FilePath &filePath, qint64 limit, diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 60959d99291..f9018e04e78 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -1296,15 +1296,6 @@ void LinuxDevice::iterateDirectory(const FilePath &filePath, FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack); } -void LinuxDevice::iterateDirectory(const FilePath &filePath, - const FilePath::IterateDirWithInfoCallback &callBack, - const FileFilter &filter) const -{ - QTC_ASSERT(handlesFile(filePath), return); - auto runInShell = [this](const CommandLine &cmd) { return d->runInShell(cmd); }; - FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack); -} - FilePathInfo LinuxDevice::filePathInfo(const FilePath &filePath) const { QTC_ASSERT(handlesFile(filePath), return {}); diff --git a/src/plugins/remotelinux/linuxdevice.h b/src/plugins/remotelinux/linuxdevice.h index 2e9aeee3616..b1e992c024a 100644 --- a/src/plugins/remotelinux/linuxdevice.h +++ b/src/plugins/remotelinux/linuxdevice.h @@ -55,9 +55,6 @@ public: void iterateDirectory(const Utils::FilePath &filePath, const Utils::FilePath::IterateDirCallback &callBack, const Utils::FileFilter &filter) const override; - void iterateDirectory(const Utils::FilePath &filePath, - const Utils::FilePath::IterateDirWithInfoCallback &callBack, - const Utils::FileFilter &filter) const override; std::optional fileContents(const Utils::FilePath &filePath, qint64 limit, qint64 offset) const override; diff --git a/tests/auto/utils/fsengine/tst_fsengine.cpp b/tests/auto/utils/fsengine/tst_fsengine.cpp index 09cbe4ac0ec..d431011d47e 100644 --- a/tests/auto/utils/fsengine/tst_fsengine.cpp +++ b/tests/auto/utils/fsengine/tst_fsengine.cpp @@ -117,16 +117,22 @@ void tst_fsengine::initTestCase() return FilePath::fromString(filePath.path()).symLinkTarget(); }; deviceHooks.iterateDirectory = [](const FilePath &filePath, - const std::function &callBack, + const FilePath::IterateDirCallback &callBack, const FileFilter &filter) { - return FilePath::fromString(filePath.path()) - .iterateDirectory( - [&filePath, &callBack](const FilePath &path) -> bool { - const FilePath devicePath = path.onDevice(filePath); - - return callBack(devicePath); + const FilePath fp = FilePath::fromString(filePath.path()); + if (callBack.index() == 0) { + fp.iterateDirectory( + [&filePath, cb = std::get<0>(callBack)](const FilePath &path) { + return cb(path.onDevice(filePath)); }, filter); + } else { + fp.iterateDirectory( + [&filePath, cb = std::get<1>(callBack)](const FilePath &path, const FilePathInfo &fpi) { + return cb(path.onDevice(filePath), fpi); + }, + filter); + } }; deviceHooks.asyncFileContents = [](const Continuation &> &cont, const FilePath &filePath, From 271c9055f4af38ee07be08191e4b7ea76f65884f Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Wed, 12 Oct 2022 11:06:20 +0200 Subject: [PATCH 011/143] FSEngine: Add filePathInfo devicehook in tests Change-Id: I50b87e213d8c83fa8d126662655681416c1cbe4e Reviewed-by: Eike Ziller --- tests/auto/utils/fsengine/tst_fsengine.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/auto/utils/fsengine/tst_fsengine.cpp b/tests/auto/utils/fsengine/tst_fsengine.cpp index d431011d47e..997fe26fd97 100644 --- a/tests/auto/utils/fsengine/tst_fsengine.cpp +++ b/tests/auto/utils/fsengine/tst_fsengine.cpp @@ -164,6 +164,10 @@ void tst_fsengine::initTestCase() deviceHooks.bytesAvailable = [](const FilePath &filePath) { return FilePath::fromString(filePath.path()).bytesAvailable(); }; + deviceHooks.filePathInfo = [](const FilePath &filePath) { + return FilePath::fromString(filePath.path()).filePathInfo(); + }; + deviceHooks.mapToDevicePath = [](const FilePath &filePath) { return filePath.path(); }; From e8eaf7809b8cbba6c3bab1dbf9bdc76b5c266da2 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 11 Oct 2022 17:15:53 +0200 Subject: [PATCH 012/143] Utils: Don't crash on empty 'find' output Change-Id: I772718d882ac7744c4f955190b038cd1e78f347d Reviewed-by: hjk Reviewed-by: Reviewed-by: Marcus Tillmanns --- src/libs/utils/fileutils.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index f5ccc9db43c..7347d2116aa 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -711,6 +711,9 @@ static bool iterateWithFindHelper( } QStringList entries = out.split("\n", Qt::SkipEmptyParts); + if (entries.isEmpty()) + return true; + // Remove the first line, it is always the directory we are searching in. // as long as we do not specify "mindepth > 0" entries.pop_front(); From 34fbf6261ae1f5687bfcdd35f9d14cdfba29ea7b Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Wed, 12 Oct 2022 11:59:51 +0200 Subject: [PATCH 013/143] FileUtils:: Support non-native filedialog Change-Id: I71797a31c58f0870a95ac396404721b004741115 Reviewed-by: hjk --- src/libs/utils/fileutils.cpp | 187 ++++++++++++++++++++++------------- src/libs/utils/fileutils.h | 7 -- 2 files changed, 119 insertions(+), 75 deletions(-) diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index 7347d2116aa..821a31caf09 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -476,6 +476,72 @@ static QWidget *dialogParent(QWidget *parent) return parent ? parent : s_dialogParentGetter ? s_dialogParentGetter() : nullptr; } + +FilePath qUrlToFilePath(const QUrl &url) +{ + if (url.isLocalFile()) + return FilePath::fromString(url.toLocalFile()); + return FilePath::fromUrl(url); +} + +QUrl filePathToQUrl(const FilePath &filePath) +{ + return QUrl::fromLocalFile(filePath.toFSPathString()); +} + +void prepareNonNativeDialog(QFileDialog &dialog) +{ + // Checking QFileDialog::itemDelegate() seems to be the only way to determine + // whether the dialog is native or not. + if (dialog.itemDelegate()) { + QList sideBarUrls; + for (const FilePath &path : FSEngine::registeredDeviceRoots()) { + if (path.exists()) + sideBarUrls.append(filePathToQUrl(path)); + } + dialog.setSidebarUrls(sideBarUrls); + dialog.setIconProvider(Utils::FileIconProvider::iconProvider()); + } +} + +FilePaths getFilePaths(QWidget *parent, + const QString &caption, + const FilePath &dir, + const QString &filter, + QString *selectedFilter, + QFileDialog::Options options, + const QStringList &supportedSchemes, + const bool forceNonNativeDialog, + QFileDialog::FileMode fileMode, + QFileDialog::AcceptMode acceptMode) +{ + QFileDialog dialog(parent, caption, dir.toFSPathString(), filter); + dialog.setFileMode(fileMode); + + if (forceNonNativeDialog) + options.setFlag(QFileDialog::DontUseNativeDialog); + + dialog.setOptions(options); + prepareNonNativeDialog(dialog); + + dialog.setSupportedSchemes(supportedSchemes); + dialog.setAcceptMode(acceptMode); + + if (selectedFilter && !selectedFilter->isEmpty()) + dialog.selectNameFilter(*selectedFilter); + if (dialog.exec() == QDialog::Accepted) { + if (selectedFilter) + *selectedFilter = dialog.selectedNameFilter(); + return Utils::transform(dialog.selectedUrls(), &qUrlToFilePath); + } + return {}; +} + +FilePath firstOrEmpty(const FilePaths &filePaths) +{ + return filePaths.isEmpty() ? FilePath() : filePaths.first(); +} + FilePath FileUtils::getOpenFilePath(QWidget *parent, const QString &caption, const FilePath &dir, @@ -484,19 +550,24 @@ FilePath FileUtils::getOpenFilePath(QWidget *parent, QFileDialog::Options options, bool fromDeviceIfShiftIsPressed) { + bool forceNonNativeDialog = dir.needsDevice(); #ifdef QT_GUI_LIB if (fromDeviceIfShiftIsPressed && qApp->queryKeyboardModifiers() & Qt::ShiftModifier) { - return getOpenFilePathFromDevice(parent, caption, dir, filter, selectedFilter, options); + forceNonNativeDialog = true; } #endif - const QString result = QFileDialog::getOpenFileName(dialogParent(parent), - caption, - dir.toString(), - filter, - selectedFilter, - options); - return FilePath::fromString(result); + const QStringList schemes = QStringList(QStringLiteral("file")); + return firstOrEmpty(getFilePaths(dialogParent(parent), + caption, + dir, + filter, + selectedFilter, + options, + schemes, + forceNonNativeDialog, + QFileDialog::ExistingFile, + QFileDialog::AcceptOpen)); } FilePath FileUtils::getSaveFilePath(QWidget *parent, @@ -506,13 +577,19 @@ FilePath FileUtils::getSaveFilePath(QWidget *parent, QString *selectedFilter, QFileDialog::Options options) { - const QString result = QFileDialog::getSaveFileName(dialogParent(parent), - caption, - dir.toString(), - filter, - selectedFilter, - options); - return FilePath::fromString(result); + bool forceNonNativeDialog = dir.needsDevice(); + + const QStringList schemes = QStringList(QStringLiteral("file")); + return firstOrEmpty(getFilePaths(dialogParent(parent), + caption, + dir, + filter, + selectedFilter, + options, + schemes, + forceNonNativeDialog, + QFileDialog::AnyFile, + QFileDialog::AcceptSave)); } FilePath FileUtils::getExistingDirectory(QWidget *parent, @@ -520,11 +597,19 @@ FilePath FileUtils::getExistingDirectory(QWidget *parent, const FilePath &dir, QFileDialog::Options options) { - const QString result = QFileDialog::getExistingDirectory(dialogParent(parent), - caption, - dir.toString(), - options); - return FilePath::fromString(result); + bool forceNonNativeDialog = dir.needsDevice(); + + const QStringList schemes = QStringList(QStringLiteral("file")); + return firstOrEmpty(getFilePaths(dialogParent(parent), + caption, + dir, + {}, + nullptr, + options, + schemes, + forceNonNativeDialog, + QFileDialog::Directory, + QFileDialog::AcceptOpen)); } FilePaths FileUtils::getOpenFilePaths(QWidget *parent, @@ -534,54 +619,19 @@ FilePaths FileUtils::getOpenFilePaths(QWidget *parent, QString *selectedFilter, QFileDialog::Options options) { - const QStringList result = QFileDialog::getOpenFileNames(dialogParent(parent), - caption, - dir.toString(), - filter, - selectedFilter, - options); - return FileUtils::toFilePathList(result); -} + bool forceNonNativeDialog = dir.needsDevice(); -FilePath FileUtils::getOpenFilePathFromDevice(QWidget *parent, - const QString &caption, - const FilePath &dir, - const QString &filter, - QString *selectedFilter, - QFileDialog::Options options) -{ - QFileDialog dialog(parent); - dialog.setOptions(options | QFileDialog::DontUseNativeDialog); - dialog.setWindowTitle(caption); - dialog.setDirectory(dir.toString()); - dialog.setNameFilter(filter); - - QList sideBarUrls = Utils::transform(Utils::filtered(FSEngine::registeredDeviceRoots(), - [](const auto &filePath) { - return filePath.exists(); - }), - [](const auto &filePath) { - return QUrl::fromLocalFile( - filePath.toFSPathString()); - }); - dialog.setSidebarUrls(sideBarUrls); - dialog.setFileMode(QFileDialog::AnyFile); - - dialog.setIconProvider(Utils::FileIconProvider::iconProvider()); - - if (dialog.exec()) { - FilePaths filePaths = Utils::transform(dialog.selectedFiles(), [](const auto &path) { - return FilePath::fromString(path); - }); - - if (selectedFilter) { - *selectedFilter = dialog.selectedNameFilter(); - } - - return filePaths.first(); - } - - return {}; + const QStringList schemes = QStringList(QStringLiteral("file")); + return getFilePaths(dialogParent(parent), + caption, + dir, + filter, + selectedFilter, + options, + schemes, + forceNonNativeDialog, + QFileDialog::ExistingFiles, + QFileDialog::AcceptOpen); } #endif // QT_WIDGETS_LIB @@ -716,7 +766,8 @@ static bool iterateWithFindHelper( // Remove the first line, it is always the directory we are searching in. // as long as we do not specify "mindepth > 0" - entries.pop_front(); + if (entries.size() > 0) + entries.pop_front(); for (const QString &entry : entries) { if (!callBack(entry)) break; diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index 5f6452b3650..9949a82d858 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -126,13 +126,6 @@ public: const QString &filter = {}, QString *selectedFilter = nullptr, QFileDialog::Options options = {}); - - static FilePath getOpenFilePathFromDevice(QWidget *parent, - const QString &caption, - const FilePath &dir = {}, - const QString &filter = {}, - QString *selectedFilter = nullptr, - QFileDialog::Options options = {}); #endif }; From 9e659ac759a4ce996bf44feb1686e75e9bc59fae Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 11 Oct 2022 13:17:43 +0200 Subject: [PATCH 014/143] Squish: Adapt to missing object names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I0bf9bde83bbe78b875775f245268b137ddb71d32 Reviewed-by: Robert Löhning --- tests/system/suite_editors/tst_clean_whitespaces/test.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/system/suite_editors/tst_clean_whitespaces/test.py b/tests/system/suite_editors/tst_clean_whitespaces/test.py index 7ffbf41e79e..49dd44397b3 100644 --- a/tests/system/suite_editors/tst_clean_whitespaces/test.py +++ b/tests/system/suite_editors/tst_clean_whitespaces/test.py @@ -114,8 +114,10 @@ def ignoredFilesFromSettings(): waitForObject("{container=':Options.qt_tabwidget_tabbar_QTabBar' type='TabItem' " "text='Behavior'}") clickOnTab(":Options.qt_tabwidget_tabbar_QTabBar", "Behavior") - ensureChecked("{type='QCheckBox' name='skipTrailingWhitespace'}") - ignoredLineEdit = waitForObject("{type='QLineEdit' name='ignoreFileTypes'}") + cleanWhiteSpaceCB = "{type='QCheckBox' text='Skip clean whitespace for file types:'}" + ensureChecked(cleanWhiteSpaceCB) + ignoredLE = "{type='QLineEdit' leftWidget=%s}" % cleanWhiteSpaceCB + ignoredLineEdit = waitForObject(ignoredLE) ignoredFiles = str(ignoredLineEdit.text).split(',') test.log("Ignored files: %s" % str(ignoredFiles)) clickButton(":Options.Cancel_QPushButton") From b30233e619b6b118382c9f7a2eaee2da4c0c7ba4 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Wed, 5 Oct 2022 16:32:18 +0200 Subject: [PATCH 015/143] QmlDesigner: Modify States Tutorial Change in some texts order, and trying to update to keep it relevant for the newer version. Fixes: QDS-7761 Change-Id: I05812f806dd56e2d11d63652d399a776599282cf Reviewed-by: Leena Miettinen --- doc/qtdesignstudio/examples/doc/loginui3.qdoc | 53 ++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/doc/qtdesignstudio/examples/doc/loginui3.qdoc b/doc/qtdesignstudio/examples/doc/loginui3.qdoc index bbe81f777db..d4799d199d4 100644 --- a/doc/qtdesignstudio/examples/doc/loginui3.qdoc +++ b/doc/qtdesignstudio/examples/doc/loginui3.qdoc @@ -137,29 +137,6 @@ \image loginui3-login-state-preview.jpg "Preview of the login state" - \section2 Learn More - States - - The \l{Adding States}{state} of a particular visual component is the set of - information which describes how and where the individual parts of the visual - component are displayed within it, and all the data associated with that - state. Most visual components in a UI will have a limited number of states, - each with well-defined properties. - - For example, a list item may be either selected or not, and if - selected, it may either be the currently active single selection or it - may be part of a selection group. Each of those states may have certain - associated visual appearance (neutral, highlighted, expanded, and so on). - - Youn can apply states to trigger behavior or animations. UI components - typically have a default state that contains all of a component's initial - property values and is therefore useful for managing property values before - state changes. - - You can specify additional states by adding new states. Each state within a - component has a unique name. To change the current state of an component, - the state property is set to the name of the state. State changes can be - bound to conditions by using the \c when property. - Next, you will create connections to specify that clicking the \uicontrol {Create Account} button on the login page triggers a transition to the account creation page. @@ -184,9 +161,13 @@ \e createAccount should apply. \li Double-click the value \uicontrol Action column and select \uicontrol {Change state to createAccount} in the drop-down menu. - \image loginui3-connections.png "Connections tab" + \note Or, you can right-click the \e createAccount button in \l Navigator. + Then select \uicontrol {Connections} > \uicontrol {Add signal handler} > + \uicontrol {clicked} > \uicontrol {Change State to createAccount}. + \image loginui3-connections.png "Connections tab" \li Select \uicontrol File > \uicontrol Save or press \key {Ctrl+S} to save your changes. + \endlist In the live preview, you can now click the \uicontrol {Create Account} @@ -194,8 +175,30 @@ \image loginui3.gif "Moving between login page and account creation page" + \section1 Learn More + \section2 States + The \l{Adding States}{state} of a particular visual component is the set of + information which describes how and where the individual parts of the visual + component are displayed within it, and all the data associated with that + state. Most visual components in a UI will have a limited number of states, + each with well-defined properties. - \section2 Learn More - Signal and Event Handlers + For example, a list item may be either selected or not, and if + selected, it may either be the currently active single selection or it + may be part of a selection group. Each of those states may have certain + associated visual appearance (neutral, highlighted, expanded, and so on). + + Youn can apply states to trigger behavior or animations. UI components + typically have a default state that contains all of a component's initial + property values and is therefore useful for managing property values before + state changes. + + You can specify additional states by adding new states. Each state within a + component has a unique name. To change the current state of an component, + the state property is set to the name of the state. State changes can be + bound to conditions by using the \c when property. + + \section2 Signal and Event Handlers UI components need to communicate with each other. For example, a button needs to know that the user has clicked on it. In response, the button may From a902729b35cea630dd33fa66a857df76e0c301d3 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Tue, 4 Oct 2022 17:41:45 +0200 Subject: [PATCH 016/143] QmlDesigner: Update UI Positioning document Change in some texts, re-organized icons, and moved the Learn more section to reduce complexity of the tutorial. Fixes: QDS-7761 Change-Id: I504cb0a5edeee4c6798f22c1d4fd4332a8fd8c26 Reviewed-by: Leena Miettinen --- doc/qtdesignstudio/examples/doc/loginui2.qdoc | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/doc/qtdesignstudio/examples/doc/loginui2.qdoc b/doc/qtdesignstudio/examples/doc/loginui2.qdoc index aeb1111f33b..e4318179adb 100644 --- a/doc/qtdesignstudio/examples/doc/loginui2.qdoc +++ b/doc/qtdesignstudio/examples/doc/loginui2.qdoc @@ -98,13 +98,11 @@ rectangle at the top, while keeping its horizontal center aligned with that of the rectangle. \li Select \e tagLine in \uicontrol Navigator. - \li In \uicontrol Properties > \uicontrol Layout, deselect the - \inlineimage icons/anchor-center-vertical.png - button to remove the vertical center anchor, and then select the - \inlineimage icons/anchor-top.png - button to anchor the tag line to - \e qt_logo_green_128x128px in the \uicontrol Target field with a - 40-pixel margin. This attaches the top of the tag line to the + \li In \uicontrol Properties > \uicontrol Layout, + select the \inlineimage icons/anchor-top.png + button and then select \e qt_logo_green_128x128px + as \uicontrol Target to anchor \e tagLine with a 40-pixel margin. + This attaches the top of the tag line to the bottom of the logo, while keeping its horizontal center aligned with that of the rectangle. \image loginui2-layout-text.png "Text Layout properties" @@ -123,20 +121,6 @@ \image loginui2-loginpage.jpg "Login page in the Design mode and live preview" - \section2 Learn More - Anchors - - In an anchor-based layout, each component instance can be thought of as - having a set of invisible \e anchor lines: top, bottom, left, right, fill, - horizontal center, vertical center, and baseline. - - Anchors enable placing a component instance either adjacent to or inside of - another component instance, by attaching one or more of the instance's - anchor lines to the anchor lines of the other component instance. If a - component instance changes, the instances that are anchored to it will - adjust automatically to maintain the anchoring. - - For more information, see \l{Positioning with Anchors}. - \section1 Using Column Positioners You will now position the entry fields and buttons in columns @@ -172,7 +156,7 @@ \li Select \e fields in \uicontrol Navigator. \li In \uicontrol Properties > \uicontrol Layout, select the \inlineimage icons/anchor-top.png - button to anchor the top of the field column to + button to anchor the top of the fields column to the bottom of \e tagLine with a 170-pixel margin. \li Select the \inlineimage icons/anchor-center-horizontal.png button to anchor the column horizontally to its parent. @@ -192,7 +176,20 @@ \image loginui2-loginpage-ready.jpg "Login page in the Design mode and live preview" - \section1 Learn More - Positioners + \section1 Learn More + \section2 Anchors + In an anchor-based layout, each component instance can be thought of as + having a set of invisible \e anchor lines: top, bottom, left, right, fill, + horizontal center, vertical center, and baseline. + + Anchors enable placing a component instance either adjacent to or inside of + another component instance, by attaching one or more of the instance's + anchor lines to the anchor lines of the other component instance. If a + component instance changes, the instances that are anchored to it will + adjust automatically to maintain the anchoring. + + For more information, see \l{Positioning with Anchors}. + \section2 Positioners For many use cases, the best positioner to use is a simple grid, row, or column, and \QDS provides components that will position children in these From 0645a8aef341ef01628bb05fae4c54fc54297e24 Mon Sep 17 00:00:00 2001 From: Pranta Dastider Date: Tue, 4 Oct 2022 12:08:11 +0200 Subject: [PATCH 017/143] QmlDesigner: Improve the UI tutorial Change the arrangement of texts, update images Fixes: QDS-7761 Change-Id: I63447ce34a72e2a2df7c18053276ba1362386c00 Reviewed-by: Leena Miettinen --- .../doc/images/loginui1-library-assets.jpg | Bin 58564 -> 68266 bytes .../examples/doc/images/loginui1-project.png | Bin 41456 -> 35138 bytes doc/qtdesignstudio/examples/doc/loginui1.qdoc | 310 +++++++++--------- 3 files changed, 161 insertions(+), 149 deletions(-) diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-library-assets.jpg b/doc/qtdesignstudio/examples/doc/images/loginui1-library-assets.jpg index 999959b20bf76cd8713b154fc37ec46500872f2a..4dc8e4e910c0019c5e3e05d1d0ae0895feb1d82d 100644 GIT binary patch literal 68266 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 literal 58564 zcmex=BGs;UxhuvT|^6v2%e8;N{^WNh`#b|3?@XF)%Q)GJ*jE6fiO| zv#_$Ub8vETGcquPr9cEk7D-Zofr*isnTds&m6e5sfq}7>k%^gsMUYiU(a@1iI53f2 zsZhkIapFP_Wv7h?MT0JWP%%y_YU1P)6PJ*bQdLve(9|+9H8Z!cv~qTFb#wRd^a>6M z4GWKmj7m;PO-s+n%qlJ^Ei136tZHs)ZENr7?3y%r%G7DoXUv?nXz`Mz%a*TLxoXqq zEnBy3-?4Mop~FXx9y@;G92y!s-GZ`{73NkPWGBOJ?{=db*!_3IQB*-kt zV9)T&sX=S)8~uWBS@-vMSv*ZW_)s-dRNCTJxk(TYlgI5z49ZGZ&9?bZ{dLppezA|- zwEfYy|J{AM|L&Q6f32oo4()swbe?b1iOC)dr>sB8(i=R`TwXS`8(Q&_C zUABIF>D?0ccenmV_n%d{e>(Q+SuL}x4FS@xni?b(Q{4<3&N1Fv@|Jbu0Uc&hmJc3b z2cEMpWk_4Lk$uAD(=&QpR3-|}6m#xz-&zzqW8%{{cT`xTj1D#`$mAdCRGEIL{Jc@6 z-bGE#9fpC=)ff{P?rxWEdg`2_ZNb>Vez=4ofYE`KK?D!wFS};avCj&p4sWlwX*iyd zQ+j(hSM-83hswkWO!uyN&;1wiNF`Hd5<_U*lX;dZT77HYtTKw=VwiO9SoEjUeE09J z`=+l_xFYt{OkHKAzt02MiaZ|#u$_^TVT+Egd|oo`LxatZ$1{bE_b`8Ndg%OV_NK)W z7A#*E@MuRop62Q~|D@Xq{Vf~f_&5U`Z_mlop2j_C)e}|)@y8dJ8*dTH?Mz~PGPN(+ zIUwZjA zKji%j{<}5nOS5lB7ptoNFv-0j*LbPnc38lbtr{gWc+Z|pl-i_zqUTMNd&I?10b|4T z(f|hATFuhHS9d3U@!fyW{FChVYLz+jmjvg3a9)!?>7TKZ&c;^NLX#k;I}Al)u0i4R z_pljHoYNed9erl@?I%IouD{Jo5O=O}iWIS4%na9wZNvSK`ZW#?UK4Ix71MO zir~*J&vm++pNT$r!(yygmRGRifM}j+NNE3?1u>@!jJdQEj6Tkk{BTey!0oxhC#O|g zdR82e6aDQ}e|+2g{|vF|{~0bMFW{fPf?)wu0~dn^CZ*D4Y`i9+A-1XMh*p!Ik>>;c zpbF;64lEmO_%3>ISs8`}88|aHalCPSaDn;V+9jJO9M-t?viGpUTNUOlj~D9|oT?0C z_2W=#Fmr48_OJYQY?9cE$QFOr!|#NS3p#aL+-z3!OtO0^p}v5v_V1js)!c{PD#f^@ z{Jh{6E_7NjhEs+8Z`hkBwQrJpCa_NTR}$ITxaY;~tbJ@!mLC5Z!d_24oG{~#>?RG1 z!>boDsQ&G^{O#B~-ea883@o~rT}eA{RQg$f|L%vS-S0gP*qSxiom;@?R=@SeYvb7C znYLGiZ$%xE6W=Bsr@8LtVV&;++wacg&|1Trw+Kb=&$}``$=^SpqVqZLZzkKPT z_{6o9q2BBEec!x&+Mf06SDuKeC@Rh9eKz~=lCE8=I{q_Ulu^EOZ_NeE&(B-~J~DZ$ zJ@a{+An|C^)0#fPOL=W_g46D7YEhS5y}mN?l-^70+jn<++UjQ(zZZ=PF1$ZW*Hu@p zKl;V3cW>66eDGoG*|}SfOpbD{EZ6zA;*!-tlm5JI-!hF|pKn^`vSD}2lodLSrz|<7 z7v*{`l@(qY6_lx$de-XmvagrlI+i}|d73i!jM7@sTFcB!UV3k1HCA=|=AS&XZlBts z*-xEIp^PZmKR)-j>!Gqfx|@#p1U-@BV}to2IBN|L8T> zA~x>Txjm_A@2>CE(7qq?_RopkTeT18)#XjA-?eP+wX;?eCuc4%&$Qm_yPx%F(OKhr z+B(y`#bzz-GW(drZSv}b|F6ed@7Q@~d^p8%U|+h$@64Le)bsl4q9-+euDBL@C-(ks z?XO8wLek$n{t)ax{ba~=tJp7#R_InOUw!A@(g`j-PlQB-TUlp3NatES$v?qTy)nRe zX0FV%>B~SD=aZx z<1xfNWaXP3lp&YgCwQuNG=;~8$#d>Ag-e-C-O z!-VnVtpj?h5jQWXTAto{Xa&P0KSuS2f>qpxhgRq~D9B2``u06w-K5)#HuXG{j8=1< z{Os?OQ%m0UcAF)|{A8HcT|Beq1K;5XchdAlom!eg^~ANDHIL*Nt+7z}rrcyH!uYM1 zq4JHL>^@(sU-o8+x@TrJ_TAWG7Z~|2ZIY2>aMjGf#-B@ndQUi5xJ7kIrlwwAsQWzI zD>5<p)1x4`cFM{%$Agl&rWp~m~|Q*6X-re68LEP7F2h^LujDNov)vO zn^rPtGj}b!JyrA6+a){i$ECX-^L@4c{oe9@>jOOXvlge!-yRTl=~D5Nuots3qrG)y z*N9Iuy6c?1@6fAf+}YDJbJs1~HZPn_lBMXG_fEx9PnpEHgi~%`owrEopH{iIdQxCm zJkOj5SLWSM4C%dkD`@kU=uN-%v|it9nYYVJ)6-(zwdtE@N2#B>l^11~b^YLXr(+T8 zbS7R=ZFbLjcDM1j?bkD(TozZmtWlVtahl`Y-^P!-nQAsJKfk(XrDbVV;)$`RL_q-~4u~f#(`&_A(->kCz75grh-CS$>pJ9Wl@npXFPrR-hc#EF@rSwzm zBv010o1Q%@TO)P=O(bx?I!w`x<+oH=#U`(kz0+^){d-gf)l?fkG4y>H*=m;c;dvo>Yg$w+OL z?0e@`_tfv`IjcQk#aX2Nw}k)MZu!sf>$UyAW?6=sy?9zm?5<*~*{<%I8oB;K z?8G_D(i0fEA~`KTE5-3PbkQ7Ea0n;*=25C#Bl1O(|Z|fCRRaC+3ESym?o5q z-Tf1J&M$-M+x(UMd@E+%oEE)vO5T$hk2*Kp=Hs|)oFL@*pTYgXV(tbh2AV!>)g} zaf&(TPCijxG$}^Idi!ZL_fv*F<*yxN#Qy}uf0h5waH^I;{-b04mHPh-nqL^|kFx$} z2>j3RXE`Wci-`Yq|IhGgz61MkQOvm9K?Z-14_|K(B&YtXbN`+AztXY{VVjz6M0*z( zO*;AXG*8yXQyc5odfu26CAqm@g@HlVQ?0?o5mMiMvyY*|E!de)Qw3>+^QOuzijc1*W@ddpXTs_7n$i9Qql zGpzHTo|aaje$J+Wfop%oqza}6Qg%vXN~dADNX!N z97+w!3;2#7{?EW~I$t=W{GaNkNhfu-EZZrxe;J?Od0*C0}g|7#M`kc!UN-b_94Z zysQa*u=+mdj9ZU$Hk?}BxtKj+a$cPB1P+BZ4G+02-Un<#DhV>R45e1N+i$*oG)*Gt z*{8R66C*{e+$JU$SLS}Q-YmpWaB9`gTiykX3s=7UWPCoomM7ru?PH%^bp)4aD!J8_ zrt}7KXh}}|ru`&w6UPB|UI$sXxw+r6E0(s2?Ao)tQcQHB^O8(4bMtBUm3JE|u=A{1 zJ$K1=2C;RyDZ3*R_HFOG_D&<}k$RU&i;=oszl>wDy++a+5woWURRkOO%-1qR%sW?| zm)86E@RszVsfU-Dt1Hx;-jNr#SlKp%`DzAp!vel)Q9x>&>=bnD+RvRh7!Ojk6o8(=+qSRn~l*p(6Bm&Ynzx4y#;o=A5>4 zzJNd7JCglEo;}(paV;eE?(cWACspfL#=0(lxA)!FDSvNl-}CZR;kHv3rn=0pK4z91 zb*VaHckO}W-D=eu~y`et(CfF?s6ajF=O}X0f7=PskX}+su8|N$XV6lIQiCmMjf2+txpIhi-o9>g$P~D>ScM zJ!I2&+w)k#^ZVILpQim5N&2w0K`_$m^6b~Eqq~<(l{3tn=YPxW*hPt+{youiFL6#z zEc|44dCJp@QvsJJ%~5-QeU`wAOSi7|T=Kp5DO%;r<>}MBjBbU-s^;ou{x((L`}c3u z<-4y7FaL=5E17of{#))!-nmQf&s3SXMfY1{_C9;v#PHZ@Zb)3j-u{inT-Ui~I%X~waj-yzRUb9Ej{dfm-0WxsWH?OE;h%r7^_IVWuv zt$)k0W=UXWs?B8OY2ouM7O=i8GkS2`VnXjL?mQko!%0(@?GxwPwC$bEgy*eK_Fg}` zs?6)!#VzmFg}0>EciGH(GdovPRs4Ns_42E+TiPdXPn%-)Tub^o}RgX+_%j=lfud`-dG)##a$7;Sp1e( zac1VlIQ^NipIvWz-%ZK9B{y?r)JfY_XKkK7y`B5*bwpa1iEB#Iq}%WI=zgB0d(DU` zInOU+?qj*sKbs~`du29dOJUsC#iEj4;hT5gp4w$LWoOwnL+^C$rK_eNE?0XjaqD+} zNrA}`5yp)RGoHun6l;2P?X=d}XZL&*T5|shJ&~XBpMm4Jsb{Qt$(r@+-iK^?_jUiG zlO%B&Cy1gfCQspM;CH-%GRLl5I5pWN|D zBKL*1)%s~mmQ3EX)ca1n=#|)8{olWL|8|>l^`XS>*efgFmuxQFRP^t8=(oHI+dxHF zYn{K(SeqrzTY%xaI@3DA;wcH+b5%c=sRaf2`KQdT>keLZ<;~Ql_uM=I=M?WPtn66T z@I+swspojPWli|vFfKo%sSD*QonGB%eb4sx+MSczZwEi$aCKJE)4bxj>cLaDFLk;7 zH2h+$pSG{eN(-PKUnm;A)@#J%bfv0^} zY2LFreQ)aZ&nK;a?|!*>U)RZd*UfrfS4VB?ofYy>)pyz4J875vXPvxTeSUhZ+169D zdRIl~8ftRf-f%CcSUXvkCu^a{d96xGS?h;ET)dGcv-TgAT)uY8l%ROsb3L|^q0d64 zo`vnHo%3?Xz27TO-nGuSHp|#(gY_ZUFrt+#zqfeNQEl(MRjL6=6AzR#G&wl&y}fxr z_PprJhZ!r+2~Syhd-j9;du9LLZr%AfxzMjg-OuE;*~^nF5Ahz;UA}r!*0sCeqI#}v z*_f&_Y0b7b%fgr}matwBI>OYzyRfkNMa8^0KS)8jbJYxGtG_EilYt*v!iE&|RIW!F)?Tf99uqF}LoR z6djjaoS!UwBJJm;^qoN$Yv&kzzTvdYSpS#B>3o~D43(Gv9{yr~s=>5mN6=%Rd6Fs- zLcY#^VG}=vGPVb@&d^Zjva}Fky!)g4kW}o6-F*-3u6~Pt`S$Vj>UA$3-w=6Z&Q+eW z&8_O|F%9O^UoC{L2h8JWyYXnpM8P6I(UjD{NuCxAJd-@%WiWfa{~NmhZ(;sld0U1O zu}!zmFZ(;`Rd`R`GgreID#|CVW~O|4w!dTDL%pXQ8&glMpO7+7bDe{F{HDxnlcS@3 zKH03_u~|CefZ**cgQ>x6%ijD;wv>_WnQiCYx}#0HflsSJKWqNSpZ0&tCjVz}&SF>$ z%edkuzZWJKOlS3;(f5_{RL$#CLZ?6H@-`jx+N|faYmx**z&MxIcvUKW5mo58C%rDc&TF9XQ!Eq z`yIBJy%3G_TvFLRE%p2q!KWqMas}^CRZY`O7qhz1@m9%YQ`5K0-E-J1ZQzsem6z+LsXfknO z=xyl}Cw7N(f9eUnZ#4f_P|$yd?bEt^bDr*7YrguO%9=T0*Rn5q-d%c4^Q+g1)rD*0 zVk`gU%-(n1=#KBF1^2by-}<9*yWj9fjON`F50aAK6@7Yn;&%V!vi<8$SNB){JnH(N zp*8CChp2ZY^FPeF=bo`rn6c`5xVh1}3(=d?N?ILTZ<$rdNZgp#e#&MYKQou7UBp_Y z1rL6iSDy0^U9TOqIego%2<4@>uilf{8x+tx_41`xr_P;yxwDhMbE|Z9{6>`pt{We- z92K0JJYlj*rnTZci#wb1T_%QZH*>sG^z^;9-N~|D-Ft$!?wphvX}vV{CU5onnfYa3 zubo|Osr}}DY5KMA+S_(ky?K}Nb6Jyhx^+OBL`$M@>*M^X@@EW==j#*|z57}lv-7@x zrCrLOt=D~7wmgkLH7m}`%X7W;O4IfAPH+C{Tv?~u_3)m)9j92z%-prpl9fs-RU=#` z%Y3T2R(pKf$!F8Uu1yc})fHW~R#$Y|%6+p+HmqH)d^L05`n$Ko*UejM?OXQk@M!_h z4UXF9nv(=nCWgvPIOWXd%5~zVlabk{qTGCqQ({%?B5ORCFWx`n-rDVZmP(p$e%73) z8=W>gH|qYxJbjV-2V`6lI!;AQ>$`MOw!C-kQ;!XtimVk!d>T?7@XYd!v+2(^`C7H? zcIMt&*(LNUlngTBEk^B_HOn1v#X;cZ=PDQ za=FumVo-M>$~=A`GPez01=iOWHf~L&gcIs2^&Dfi9r{1iMoZSiwb^lz;ty9$kxZO@YkZW1Vl774E+MK(Z z$Ci|L?^_nCucYoVp#om!heQ)@h2`Y)hF-&KK+vk!_!N#9@f*F-#du& zu35vN!x7lPX~Q$Q@i?=}9KI*>zA)U!VGZ-VYvHOhn~jA$RVH~pndI?g-m?bjZxE{) zehdFv|N5T@Lv82Z@UI|y?T-9g{p&viXe6^f^l$y427VmIG7>gIWAg-=CPumEe>51( z@+wv8(m{TOnU7b}Qb(nDi|F$^Acy$=ovgpAF8x14=K_AO1$^~~_usgmk^jTP?)JBM zoj1SvCqMhSdve};`zN2i<)5?5y={0bgL(6RhVIJ$48h|6f>$sMVm5Qrwf&p--BkZ( zeyi^Ar=RiJpZ@%3*s=T1dF}7@ug%}y{v%!dIjzA6I@x`D%fFMKroXMv{=D<<(@JZr zIk|T~*WO9D{oJR$Nk05(-1S?ZOP@a3_ABbw+x(eN!+w3Y{IiTy^8$L&?)}c{y)PLuFHQX|C?X`q)q6QajPoQpA75ZonV(=(9aK@ZIxhaG{gs)i5-*mkyWUj$GS%$Mn<=yNH+gQ~ z6mGP7$_IG&5bI(SE(Q&z47(qH>L)JXu+Ol+^`Bv1^dAj|k!c)TsLY)I44@!mERx@;4x0XRb9WGZ!My#$^*`0- ze@pFuafR<}{wMvPLHN(aU+Mh+8Frojx8M6egU7Pc-sN|Wt$NF|YLmbUF9V*4RHr!! z(_|Vfl_$pDxwv2V>eYAa)~#K<>(i@RqxGKKC9X6V`kvHu`{?apdj&XXV}P61S(FIDd+}L{x8y z=-;O$v$J0P%3oeLZOuSyJx^&B7RBr`&3<3)9=BHaW{|gbM})gdkn-e) zH9Zm&7!{a|CvaGNQTU>?(n~kiG&{a@+pb?3|DNBtXPSAk*r~8E>aM40_SRixcTHc- zdf{Pmdh>PxpYo>DzqkLKlB?2Z$zU|eow?{&SIo!onA5&rqq43)oAuSFe@$TcUau;X z`TJ$1_fEd<_A_?!o8LxftUNQWKAWn0`qYQkiK!D|`&OvCcI<>Rh-wUr5T z_FmPMy5;8?^)~<1pKVu;Ma=d*Hb*s3!)({ONoxg3_h4o&!Zo=JDre3L>Znk>% z!@b|G=eC7LXGQDtRig$JKFl&{EKRhzf+Zl$c?l(d}-t}ObN zxok^W)mrn*S3X(&bpFr)oA1D}FpZ007XLnb`6n67P7PMG7(O3=wf}wno3#!?0qlYN zt!bYwdGGG_7MtmORQAl$l+wJ=E+vnoFqzf!}xO)4EVcYg!|HIU4$jT7CbJMJgN|Qow zZ8!Y6QKosP`JT!Llk6()ByOlUXW*!OamU`P>;E(4yL#4soK)ZTU~A0j?5&^u&VSS* zxK_0=ve+Dgvo&0wugBB7jR$jpMh4Ez-TYEj*>~GJv&xqQcANgmS zp=j<3{uzvp)1?jk%4>F<7k!tQ%kjx`soJ}#SKmCEb?N(!zs4$Cx$F+DS+MF{-6ezP z&9hrp9`gLO(rn%2vP|o&t9|cy(d&UwqZ8sQOg2UewRDa?yjGPafx|Yvq0VmibmMWoOi`xK$?Ck|%O|PnmyamV=A8 zf?XNslT))RPuOeo?)Y-hZ+2MV)gp~ox2ob#eyIzq+ZBDt%P6pI%TLW?=N31|op)W- zvVQ5TtJ!tmW%;)9gtHZ`bJO`?u=DKx$@_ooI6v*SzvYQ@mWNAj$85hBDu1f((tXo2 z>px$a`A}UeYf_>A-$lP~f8Td``f|V2diU&}?Rz(E7wo+}fAdLY{dbd}3n?*3f4{HD zv!rmpGJEq>mc{1vZJ*k;ufAOKHSXPh@#$xFURrN%e%iO%AN0k+S-@DSDSTz z+PT-EtL?Wgm=x*LxXqhIuqiO6tuu-Jsgc4hFPWO3@|wp3-8W}uW`90aecRLW>*i~h zVy)L3{Mzbqh>N$_ZE56g&!eV6!SzBd^NoVSzgMsR%DMHDGB-!q#Pn&gpPa?rJVd!x z8s#P=B^?m5dwJ)$g{W=bm+wKB{OdALzD`}fXYK9i=rU)vowHY#ihIqmOb)6(78)}- z(|Kl8rmppiUHfhaZuN*Vy))TYAS|uROs-&N%4eyx$#s`Eq_8lg%rT7F@K7%`JIcs6 z>*{pfS)0OMyX%zroR*!v zWnC|iuga^yg^F{RJMpaCS0=1*gE`kwPa>h_Pw~NGRgw4aR%d@$;iapoDt&bK*JtH% z?z5eY){1hR^ve>NHEG41pyeu8R8M=^o%5f$wKOQ)@BLki)4yAHOb}67wP?jDjt>kk zx7ai@FRe8(bvQ6c}H59(a8`D$~+bbaz?2v4>M=s#HMulvzDtU2k`*mZxm3Eiv0# z&+}>))8bcUo1=7=v=}S;9^sk0^PmMwx~!4$mCzj)>(aC1Zh7fff6L!&zOrqlm)*6P zKJ&mlQO$E_SDMUfd2;niYWB;;2CI!HU|9jo#jrU&-E5&z)UCzdCTm0m-6Cgv36(wh zg5k+LcGJM_$(!!St;|{XwJImqZ_aAZ;APWIXJ0+0b+LQO>?yOlyfnAnjutP?We>Oz zn{;LE(=C%+@2u9ElC*`s4`n1}m z!Z~W|<-)E$)Ks}NrKIf6`R4ci$A3yS&$zIFZ&`!Q$6xDz-~X*0z6*+l#AL zc6<#uu;Gtec9yT(5}AFX8rxr%7F#ZRs4CyNJU=?4M9z5I&gSr))obS4xy@2P$gN@| zbkU}6MZmf{1v_pZ3}RO`d9-)Ve}+!6E7#-dx7@GZz5DNy_g7v_4%+hYgUKA5#VM0M zJ&k)EE49(SBe^=(-fzn;*1W}i>00;eEL11ha6I_5z;1ix957+O8 z|Jc&N+t&=HTI{>_wCdPti+aAlszE{hKPnSu$N2?L-F@?9e);T&r@OneH{{N`8#(b} z>t(kMp3*DBd(X6n^YPhznot=Q#?Te*rFYh3-Mr~r7G2%y_sY{Va`{4icd>P|s!T7t z%`Z)x?e}tdwOnh$FW0Ge7RhfuT=vcL)9t&99A(enGFG~MFLv#fjAM5+RW`5lnRr~| zgvhkZ0eg48y!FlZ@Xh@hTRl%{t?rxU7x?iTM>c4k&n>FRhx0-w3O@wx3ZeP7~?%yq&y-l|+ zxi;anZ0n+%Cl1|G5&BuSKTED!Bi^!U*Uw4&9*8aum*gs)`{b3E_3z5cxZYPUWv9-W zYiY7|Quwtq9k;Z&qN{DIZY`UZU%U9+beAXdW>0-~ZR$gB?wJoJ*}vJnQG-n^xJ+%= zBps{f-)HYdyKmiPaQm>#b8F9#!WSF<%$1uT9=ttz#pji!n=)T?w?2AUw`uj+7mw|t zf5uN;|2}f9zve0xtXTr9U|;->`|svIbr4sCc2G+g;~H!y9e*VIx*{QiTSVz5M~j`z z=}$X$+?{*d?mvTc&HmlTiqBSyC`;bjwkq&@dDtb*`20i9H+LR$XKwB*bQAt{Xwo6) z?-QQ%OcOn3x-(Y&?CDeUH|L#wubolE^sD}T{o~dDL|??t7yh>;?LWg8;r|R%GU`7q zyq`P&_x(lx8CtD3#fv=qYZ(92t^GfPmW+Pdzwa;X4|Q*d37-9*;X?Soz=i)APPP6{ zxqt7k_#ds-e_NjYXZRutHKp#l`S<;f|3sAHMV`xp%`W)Q@MlH&t*2m9<}avU8+rUM z%YTNaGWP!&v_xWk|GvLa-)dxkZO`L>P5VFf$^U1V$|r90pP~AT{6mNOwULMZGt5~3 zXL0*~hNn({m&M0lsBdNbxAnQKVf;^bevnJVg`kS}U)%E>ZU*ILKF9;}#nQR|B|!s>eZKno7xsr5{xd{< zp8t#MKf@`D`hOuBv;0A!W%!?AZRPpDto4u}7d{UT0Z04SamCj`(FTs7cq_14P{`VU z5&zFH1uW{1EDBd^^OL>)LVc?Os%>&LGhc(kd;izfman^@PEdzBEGB$TJt*lM{?G7f zq0LWg`3v=}%>NmpHn+Xm2#v><-!Aj-fnq`b>w1g-3=3A*-vK8P;kQQL>_Gv+1+uRm z?z@=FrIlY!@5mR<-n(U`)@uGKTXJNbli4MiEfgLvl+BW{{Z#L)lOB2K1sGSK2JCys@r~7X2?Jif2a1mz9 z_X73n`v2`%|F7lh!^id=`w#6mtGnOvpP~Hpe}*^lt^XM=MfS7bm+${%`dlt?p@Xb1 zL*-|^H$`rI6~`CK9{jq1?T!7t{6FQO;c@1~f1m3g@Bib=AkF`M_J0Q1{|t{Z7;e<> zx&Pz+e+FR(w!?p){bx8{|6>+|nf!b6f7bsQj$B~e@SmY_|Hu3P)Eao(f1mx&(D$F= zqX|RKsHyM}U@@E~p>xBgo*xv5;)zIArPW-Gv=jdst{4AjSfE>7;s0>|NBfi9X8#!` zszB4&n()e>v2SctP1K)Qr&)yxU3joXaNCl`4DB;zw^)we^qF{Si*Rj;d>4C}bcS-g z<8A*e_Elky+dnLr{r7Rx^R>*rH_C3Y%@WQO5iDz#oAO3oe^0=coS@r|WwRXKF&6rG zyC*1%G^a1vVK?Q}?l+22PUSDutOIR)6!NSZKSeaUQ= z{%^PZU&id)hw6F%Gj#s*m~;D|%f$Z-8=L+!T*;i?biMLt#0SIBL|gXUFUy=i$%%hj zFgL9E(1q|x@>?#eUkTVEyy%r}xXlHVcNS_I{DEbUnqsF1-mZAmSY>dtFLQ~{mMZ}} zmM1R1o^Vu5>GiTlZ_g`rN0?uG^k$XXq1L0`x|s_^k8*Op__CuhbK~;F--Q9@i961V z`bJE-UB=YDR7^WS@Smu%_kz27u154NHoKS*_OtDrCKK&FUscs}+hP0F zIrBUAKYnQcdeVP}7yfZ#uuz?W6si~Cp*jmCR2frLLndBf{FjJdneLFxZ<4K2bL!8lq`3_!vwJScol1nU@KCQa$ z!X)QCspZFl3%zQc#-p;9 zyUJh4=3d_(G;!yY0zZy#@(&$2zpb>bcLYr(t^(KQ4Dwmvy552P))H_7hjB0~klvNj ztKzcpQg$@xOYF zJ#?$opUN8^%utW*6fJc4CvW&@rp@6)7vz#RI?NVfyfwGAG?$a9Et#0PBdk43z z6!?CVd-GOg+gC%m^J!T=?5r_u)a~d<+Ao2qoK!=372NrYB*fm&8z7X_E|VX zRx2mysZZXilozv;y{bZ{uJ`S;J~Agp{clnHU(VNuj@1j+x7OwSy)O9g{q+9~H}-S? zXINq^25C8*v$+fzp**qg(SL?r_EQ;z;6rH_K5SjqU2E*P*3))_vf*jzMGFp`4CfV| z;QwKR#rae23K!V)R)140dhGkT?hK#6nUE7V{wZ%1(6DuSFu`ih7lv1MGBVlUj+jqB z@}I%pUjLWLee=`P9;Nhj=y)owC$R=tpYQtSMEdXV%vBn!mffIeX>ArCHl&U7NW2=t_UnD+PyH z&!kn#`A(P5ZZkL$Gk49$lSo)Jw z(!kUI?mjK7TNUQ{ZE~!C;GF*qxfPS{#ch`|TbM8WYqoboui!7|f>{0q^9(P9oUXcB zKQlS=^q;_pcHjRDx364T{L-`BDE{zr?=1#fm0vt|G8I1+Gs*k+<=XQm7fb(o>57MP zmKQC$|0Y{ZeM+UnxsZ+z_Z74cayRx_1*-j>Yku`wQ~uh$z2*7dOWr=&Ei2}=)n(ha zQrSI!x60O*R{h-f^sIh#yx_s)rS2=@GNW2tA3E*6ckNl`@=CGDl&;WOnsb+MD$hti z5t?*>hr6(N>dX9-5-V(lp7gF|-?}s}+f?+--KQ(tjapLQnmwCx=i0in`kmJ1r>0K! z-JLP@`={G0mTaH-X!hTfyi37Lm$H}jX8w>qYjG-Y+9uy+ z->=%1epOg)OjH%2!MyRq_dnI!|CZSQ;tF7!`~6S#{)_crOc(H_HCX-SNxQiJ^~V1U z7sU3Skq0;EdOr966mtJ}jQc-B;IeGS=AQ6Hb-`zIb?kGE;&$^h%aknW{`7LghaC${ z?o_R1-XG#2)^M>)Xl0r4C+9o6DZFKzChpQ_u~aqndqF6t`SkxOuCQ zeBy0G$FgmQ-kx00w_PS!Rzr8)!c9#M?hdNC&8f_{ub2gBvz-a5;=5nbn!%1PKR18(asRz9{y#&~AN_w5&Uk?56W3dO_JzzSD*f*M_u^j&gZFpoznA~4U{*d= z|8Dz}42Dnhf3N*1#jtmI?A^@ZwO5zDc;;|0Iw6NW%sa8VPlZ{5^=gyf{OZ}bW7p;MzIPY)7#}+LxRgaS@j{=yL4|S? zhpFd|SG!H`np~Zg`QozgvU$B3Co?@G|1(Tm9dh}u$yeX3c`I&SsqtO;szBP)CD7sB zCXZ>?rd?a)9`iP{?kta+(@xd$cU+zdOvQn@N&gw9-qT)Gw)N7jZ?(?Za8uj1Fn< zomcdRy?om3%aeA-7@j}nRwdIFD^jJYsUIroYX65VD0b_+iQ(Vp{Mz#9{^Ths*Y8X0 zpPqJhuK7``c@w4FGq!!)68Y)bZ{FE4iYjwY7hQW+aA5M*KW9%LyJTSK`#CnZ^wisT zFSAW6ZkH~8_3NFNXXWc^+tBy5bFck1`((MyQ)XJik;#)hR3=qTs?7@8wCSSK?A*Jr z?yc>G#99)+=6{A=_a`o3MvY9H<^NvSxit(DS?2W@MAq(R;`saU_3;mD88YMRkMI5~ z{-41)is4xX^QH^-kFV~3y%J}3%x!%iH}R*%-z`En7G@~EEj+bFV9OU(?w5|MGGDZ> z;^KIp_)x1+ZM*ztQPqgDGXcy^7jy;Q?pP-N#yXY%M1CSy(yAw>X?myh6dy9I)c0+@ z+2#Dnaq}$B?S*EK8mh8dwH^K(dvi2O?HcclXOnrGQ#rz3No(HmabIzN!>660=H z#v6I(y_in@VvOQ7b4krGwvZPtEZP6{Og(bzY)Ak1n%)17-iZIJIHLeZ>x==~zhs$$ z=a>@2)Dz}`Ci$85ulj!<|EbFmvF|@afB7%{e{EYB&s<>7et2EWg>;5&E2pEOcrJ5UEIZ4W0p|ga6|LXv~`Sz9=eHK7wi>ocClvjYCE*OS>PnHV0A>MPk@+H?t+X&FShfRAMM4H z|LyGm%Zq3=97kz2a1m}bOd`EY53VhE-|GJj{LfJFpFwp2xA^^!@BTBm{%5#wt-*2@ z!`nY5#yR)DUir`PLNo3e|Bw7X{-4|;kN@dZ{?Cxi`k!INWh;Xj>plL=T)N=zyeX`^ zg;*EVEom%tINrLTn~&QeVuJosW{-*}<|h`q3~RQ@cX2UT2e>WXcES2~;cX3#*eR^7 zw;lL6Upug!Vlca)rgqrDfU}D=u`IxJ-3hY*YtGFg!rKme1*n;)GA42*HMQj&Wx3(B zh2hW^-g&8y=II^(J^#1$_12z$O8*(S{@ncCru_H5{(pvzb>jaSJkxmOYwGMzT%S|9 ztd*hEgz@>r+M}TEkQRgL@7{ke|AjEBGKesyojX_S6uNCopXaj)50yJ5?{=_ESg~NE z1num>qP6v~A*0&BvZ#%T!^kC09%BpbNVFJs7)ok~@SdQHGna#azqKEH}1wNTE7aVdI zENm^j)X2H5K`LB?C)0~HacO4b(>U$_4E^VSu>U*v^@^QN%h=z>mGm#xsnwZ^ZGu3-AQchQ}ffzjNmqBTT$`@s9_k%Blgm}u>&VWXEp3Vt ztQ>BIpLyn(r%L+&OnR#NWOsRe!PGlb>SqOZeVwwi^v&e#*s7Pyw!PV-V!Cs2yL)x_ z)R|8MKHZX-!m-Fx`ftqTrklA-9e)-}3;#Z&wb^rbE~o$56>lbOzg4CGK9cXXYi-Oz zd6j4P*2hkYxtHnvv$X5@p`f!0O>g|O&3}bGoy4@zckA|BOl@hBWe<#U*-TSvw!LAw z6*%SEo()s}6;9WVn|}98)-@^rnXWG@FSnmsy!C$G%i6W2VaoeH?7rP9_AI+I#aqnP z!!zjmgqbd>hh;46b+Wh2tV=R`dvIm-rYYe!=PbVManN`F&fV)e z+`n2b{XN>>ZI=JZ-8b#y!t=h>_PT#a+bOhpZ|ki!*?T*id{~)Pp6Br~s64h{crIb8 zyJFeB&b;51zq0oo_S`n79x`oHjQWRV?D(-&Q^~ z>D=Qc&Vw)g;vTKK|7X{f#sBo;(o1iBd92l4E4t^XeC4J1&)@EUd-t*Yedap9+4oNz z?>}sAZgy>Rl+JYHdoxcRlu4bL+^}jUC(F~w?&ePoyE%1=4`(i0wEy|7*|j^DTd4eJ z_#pEB?~|5!y?br{x$NG`arW;iZ~yQMyVD=+b^FG>XhMwiZp#I|Yg=^p=bYBgPrT~% zGrOn9>_hQo%|C`yO8tVmKRxyQ`@L-2cjw=JMwjnAi>vz-RT+8jpKGqUL))|D%-Rx@ z-}}u^3Y=zn+Wds^nW+EW-s71MX9#%uB%6x9)mgb!U3c24@0-Hk{d@OTSpChrXR`vs z^VhDeUixzVeD7%sK1}G9^7VS9E&AJM+r)RCsh?F_R!sT#*6+rIUCoLoRriJD$oSY- zto&3HswU5A+r@S3jx--!cDerB z+ACoaJ11STJ*l%VWn=DV>F-%(hjw2!l9{~wsoTMW^7RwlAJ5v=ed+Yfv!Y%f6V?~e{{jqj8g`=)t9>b7YA=$`tsDz`EemP ztt+G>qRsZ2ZEln~Cfm^Q4`_FJzF5BDpoYx9R$nqFy8-?xL z^nWY7aNr_&{!il~%+?w1acLaTx_wfQ)^k~S)MIVP3`X!CP+F&#d1!J12`@kZGxHfdvr zh_YKO(is}DI~O?RF0g6639e{=1u*4;+8|-ln;JG;nz0~EU@7y4LsK}MW;|+;+%Cd) zTh8;*`5%A(Gc^2Xc=H}UaQXJe{0FK38NTTs`p@uE=lO%*k3ar%+w=I0i3an^1^hPM z@AQ8!{u#iie<%OD|EC3<8Vn2g`krYhZQ8c2(qTO+@|&~EZK}&kxO?KK1mh=ih_3e)zib?!8mN&!6s(&f4mEBk+6l)NQNVXT?S>S9_W_{cUy3 z)+MiQ)lFnE>o1xtp?c+H#p3yi;%0(}Ipd8a?mwF%V(Adldxj~o=ls2H&+Iqp+?gM( zrpH%Z`_G`BzAzwW)9$M37@PkLZ*N#y)?SY)eOc^w|LCmJ&5r6bqEAkDE(_?N6M0Z6 z=CKxAavDSN=jWO`QhH;Tny+1~HFfJ-sVT|6Ubo-ZnqSWC$@x29KR#9C_V4=+#dlE23=e(+uyhY4@`JhR0pK`r_5ask>Gj3XPfla=Fz-^QXnyZr9hmRk;|IY1Yk| zZZ5Aqx8u`0S&?_F-ZE)}%WfA_NmCjDnP`EcE>_lvi2dn~Hh3=zaWnp5)ht=LXnQ!UK_-1jNb7E zR*jLfZvPF|EkfsIA06Ct_wU-4)eL>+H^|4gtXZgJgJqmBW<(+-=`Ms&NZPKl0$Er_DJMTK+ z^wE9A3@Po5cLJ;4H)Z#}P<8x$Vd2J4+gO9YuQj`S)zd3F?%Z4pb?MA+Ra@SdeHZ#( zdu8h0*!b{niyV_o?;&|j@-r|^Vkr}bl zP+h)FL7yx^ueDbJNq`N`ni3Oq~_Id~x~TC;8=?#Vf6Ez0)!Z`W>HY zz2trQ(^}v3h|4yy|F%6Xd9U>CS?cFSGyj~f{IkgHr{L+%$)~KRepAkqnA{x9Ul#J} ze95diVc)-P)%v~mrpe#ceQTqq+RkgfWVDob@3}}@;ipTb{AXreKPaiKzB_tp)ylOm zFK6Gs-D}vXr}vK8<)Wp*fr~j^X{mSQ%RcWi;d#Ab{_LVFu6CY|H?5|;j(weRZn^E^ zt+)0}-!ttl+nYI3yF-??Z?8NyUETQ2+=ZK~?w>f7oMtj}%fm{>Y25p_xE<8n7Ngmm z7}2?%XPpG&@2{O|6Gx_^JbH?-pl&w*H~u4Eev`K&LdWFJ}K^{!jg7bfVkMoiFUmSJV^5NnVTYh1eG?H1mJ4>LqtZ#zT; zOktAU)-ppSzo*YFoMhbEDfxX%!Ldaa$@*K(e->(~yo zb~0=!cKdK@!PJB`$z@!(qq7AL2x&0g-M27D=jhxsL7v;vrp8%Lv)|Xz>a|7a`8y^K z<&`XQTNpNF6*%X8yC!I3pBd_TFVf}AsY#qpYBv@}Jl30N{WN9590B6{=gp7?F#DZ%_q$ZL~O zQ6+0qcg8U`j^qh`l?NDBqqGH}O#-YG?sh_zcyn7~{rB7d865jyqp}@m|1-cxFmAQ3 zclk4??iSOLuDvg$RJk2aGF>oym?5d)6)xM}x_o2Ur6Y}+F|3@PJrz+78eZ&)2W>S( z+jEXGCfs%`+ps%>@pfj8m~fiLjy2L6JJzV3Vmg$&AdG3t@x!bYv#Kii!gWr!o z{?l9ed`3n9d$5CCrTCrr@9v)#uv@(g+H1t|=LO>xYLE9UsxU}X|Ibjne@f}Qg#Qd} z{~3<_XHfCW|IeW0@t@(w!TMJw=N^D2l*Hcnh&8S8SlPugwNtcKL+ zRETm`;dZzryRGrY1YO3&vP*i04?UWCG|?%r=#6!*xavZui-lamA~DRlit7$;YDjv! zz;Ro{hO=83a+eqKYP%kA+7c5c6)qA}rmXGAzv0qWr>!lw9U{_hv0Qv)r~jX!|JyJ5 zzmlpIJNEa2r-+sd|KZvH4?HJ4=LuxoVqRq$+PDR|13XL(=D3C!F76RB7XHt$@BS&P z?*_AiLE~cT=Kor&}eVta)@EeeeZ+VS?r6kU%uHhLP|LaYl`M^vq4@DiJt?aRq@ zv9O;zi22Y3Ykp40*$YZ}aIa%FsP%IEUH^X~_?nG;`3>jVYyXAKF9Hkq*+VdF8wYanXBq z;Yk4+EFP;JT~LW1`E%q_;Ii zZ8*DyBkvZ|H5>6`@t*|q7uUZyseR(;pKQ>gf;ScGA3U&s6W<0}%5oN#s4m-()VSvz+FTO{)YiMJ9i>3Q{cgTLAYeCb(wNpQ;jC875pG_Og%7St2I zX33)cV%?X2WPQ8u-5rw}zwwSoQWzgMJL~Z?^D7+<_g%A*T`uB%r?6-HoS5m0%6|XZ zGn?c0DL?zfC4Zy*pG`N(`(-vgFu%sQdU@uxTYFxu&aFAK;m?DeH~hR6o;n^2nUg2e z)1~^(GkDrNoBP_Xub*cgy}j8{zDlp~maglseYv|=AM^Tq+4*ZfcYH?KhuFI;f_E9W zC$HW#S*DnEqPyYFzKA>82@fvn7v)Xu z`BwJsBj+{FyyVC;IlC|J*)wK|y<_|Nd({8QK7J?h}OKJel#=)#)xMXhgR zuiNX(iFJ3bJ928r(!xosON;`xh)h(7;!@%6Vt?WxHX))S-pf^p^(cdNE;FdOtcZ$c zZRK`7bm;A(r);#6Jwxr?6M$h)N{Ca_LpN88(Fic2#XXJ;@j z&0t*W%5ozyCO}LgtSP60z2`r}6Q%zQAN2o9s#Zqq@3VgpKifX>XgxdQe})8!{J*Mm zuE0u3H?&d`HoCj;VQ$@Rmzn$SX0m?W(>!&O#@(w84;Js5!}H%PITfT{3mzfPrH1M`v})&Ch3PuSnt3CdC{rhi!dpW%Af{a<+w z`>f)>{%Zd>ukm&!hpDaHVaK!(skvKU9XaQ;>dxV^w1!C)(%be#+AcL+^7yUVg1b92 z16IsWSry*v|7WS?sk!oH`O3QU^oWzn_j;*>(sCA))rR-T{!@9uI@))$uQUZNR?BM&A% zSbJ^BzKv!3kB0fK58{n`?R$M~Y1pQn)icE&>3Uhj-9IVlD*SiuMd`DXD+Er<)rI^_ z z`{Da_Z`HqVug<-C?6)Vndw2GW^6r#Cp2wWVrn|C}lh^0f`Ge&M=m(!$egQdS&Xx~HT?weFnQq|9~GFXo5GUDD5AqciWe z%e0?Z+vWZhI8t&*dJXC_Y^KIX_8pe>6@T>blZ&w+tu!zIy>9* zYSVF{N2b#pLO8h-E{3~?E&i^hed_-CpqE9T_Es+Y&oJ#;{>;@en)!aSzAr45|KPa$ z?v44L;)rt>b>uzptoS16%x6YHTB zisbq~oypMol+U&bTyO35K@B*E1sPLaS$_$&E?6z3^=QILrc2yHtVfyUSQ#vI85197 zRPa}OQn0A)MPJI)@9 z9vE~jy4|FGcy&2YH>rOvYi z$S}|6Q`m=jxa5*3T@T~q&gX}j?hTA)mFRRN> z(_pkauD;^WdC)lxo`&0%C^nIHBqFnnt(WIyeM&+$Knj{iG+{ompb8clpz&01aJjp+=9@ zLXBN)Q#(b1m;y93Dn$5>Gz9JRc6^xSp73iM%Tbmai6X3Rf)2b&je-+>CeB#?gh|lD{oFm1{|pl*|7SR` z|4Yu)XG!}k{xdYz<@_xc|0Bcxw;^%=mubZYka^k|_9@t#5kdh>tEO25czzMNR8-cr z`%UMR#jIz|7K`rARoRy_{mrNU42Az0-cSA~x_tBePu~9-80()D|4nQE^LP1whQ{N6 zj{erU|M@Ifg~;Ej{!ga=WBSi{%0_(J8Aysb3JGacYkW|Pu2en z4f{WdU)%7X;q$Ng|9JX;w%=6$&oD0^ytO;<&8m8p{U5~tIeuMZ|L5P*{|wE_{}R7> z{%3eT8?w2%T5JAE{XeY#879n3OBi#?SM6>z}Br|Ka}6;C3=DPy2EI5AkQ`uYIgPc%c3U zoAiGM&vZjbU;RL(8jcE9l_BG$;m_?J^LFq5B4&EM?VnCv{V#8uIj4Pe{ePGLXYlU4 ze{^|u_{E4nPq)SR8sA#cZB)4Qci=*sHTB=(tOFMmZCvhsJj;aJ-rt!$euF`z$B&nr z|1&rT##GFI=>7CR!&2p&r`~NWdH?r6!(S!$IoHq0FCq3oTRa`<>4!H?RKL3a+tg}{ z_=o#H?msC$^FPChY4+db_J5UgU)dPj{;Z`TfaO3^<0OrhJHu2%ml!#0X-{&{(5R4D zz{#+nOpaBtpQ&jLs0_Zb#4xjA$I2bYa~C~KEOb3|uuG4zOOM&(s5s-2;2mvK7lcWL z%>Z?947ykh)-p+NYXp_Nhio-O`{&r?{%26Mv|Ib1VR6cOYtfI#|8Rd!e=V{9VS@b| zX5Rk{OEyo3>}-2rGua8el^M%I!AT8P{M_!(UKK1~Kl4AsqB+;&q`fx&{Lk=G=hJ4< z8eP|a@_+Tzw>vMd4&Q&KhHF`-`?7wom}6ZN3rh~Z3_cUTN%rpy_N{w0DTZ z$$#~KhPPVZ3icnae_TJwk1(b!40pF2$P{6? zw1wf224hRG+F_1m9)}Dac$7tsm~=5DiU`%*d|dyN%kyjfUuo6K8|xuW% zP0T%zCMM$()K$cYbt;Tke<+q`n?3%w>p#PbncB@UKR{1o|y*Ne2)0|rmRkB%ZH;gy3b8>5f8c})@O0`@)?gsHu*cIdVKoN z&|G~~{LH(1VSH|u6eqrBx$MGvX|S|pW8X5>eKz{;hT2* z>XsH(>&9H&lN>zt_9T08?^Ak#o9@2Mv^DW9Ez`XJ!z*;Jq0_}RLPtwWdZ*o%F?(-O zlDuW^t3(ZUOPx8NXFY4KJ3p&x$K|Ewf8S?H6x}|1s%EaZQLX5?l}q2sBrYmV_tk&; zbn@OmQ*`atPe1RZX(Mk^U^1_U?U{x0u1QxoYp%5UwnpsiaEj3Bc+;(zuzWXL;DyJT zMY_2bmnXlJ^zt&RjS9}f4Q`EdKjcRMX!3n9-!Y>!z1YzSeypQ}+JN zr+c?eesewG_T42(PfVO5Z8kV&7s%_FrJCoa1r_Z~czLU+J>pc&^r-4)Wz5Nw4i>yydt0XBLVc-MjRVLs_RY+mv&m z_0=Mk8j6ZHYL-mgvf-TNLv@Yd$-k@Aww3jy+}-Z^IWj(0Zg%{#La*YMlBc^j-_@&& zHs4%rYw}qq?uTBkmH50z&u*G|T~Mm~a?)hAh*3*#nDP~$#l=$`9Pb={dUxx5qvf(G zPc@e8+HtjH_O+AR$9`?Om34CW(yq*P=}STs&F-BHX}1ZlzU?daH+SX2%m?$;K50Cg z#l5uc+pSyQB=$Vb=(+Spd+*}jnA9knvB9Q)Tk|=plulBypU2L^vN><3S zsaHZ~)rRXl7Ok5$DSz9l*WQaI_Xum7@Uk8;x_w9YaOG`1<%pwek|Mj8Owd`vaU#`l z@%wY^3XO}uM?br$p?Ux0`H9-P`Datp?^@q_o3eGg-rAE+_i|&c_v&Wv{%Nb9y?2jj zi|4g(H`m79T^E%*UBO*o_L~QrB$%gHZkAf4Ez@*%tHF`F*I7}~lh3_d^sFl`PxIux zlPX{L^~8PMS-N}DmnY$q>%3l;=Xt8`U2**3P2H{4v6emN+9luK3g=BtoMw6IjgHfj z6U(h*awA3Bm?!XByn4#E=F!s9mwYLgqwnc@*}hwURnYu2U&)uuE!Tc|9$js>DlmK7 ztk}ru2_K^G>bT~uT{Q2b+N66oTaW!@3G!-n?ub&=NlKXgRJxDxSaIb2X}a$o`-V(+ zKlSk3eb-XyZPU~9rr%s&d)#Av)=RxR*FE*tU7r+Q`}K92d2^J7>a^(HC9`f_v)-AY zT@afz#VtvC8na~C=VTUk#qyqQ%%VGg&$(NfwsLp9^}boR(`Myouhm~%C|`Lr_spEB z{~4y4@4oa`I5R)rmQN33_yKpTC-g_*CI27t|1Pah=3Q$3&-|yd&*^_I8~-zG`1+q= z#V6a?uwDIUPdY3y3eeD)!o@I!%T-8YL756OxXml^C$O-OY3hM84T&uc&JJ?*kJfkG zif>Vi&OWR%+3LNf(34e@TUF++o_zAyn+NH;o~Z^ITzpp=bl7jxv$nf8{SW_V5G~0U zjCD@TeXjCd`p}w=RjUHM8P3Q&uVBx+aHq%YQn=-@H7P&Co=r8Fs_A+zY}1?b0a14z z-k1_{XJerU>rrX0L%};D@-%itq#E)n&1e&J5S-}HHql{TGsjWu2?kx|j4i>;&*n@2 zXE+fAs?lED)IN8#2HKU{X8a>U9@3S13YjtA!LI<>s{ToT$NqQyPaRnH7w*4nf2u)c z0pIQe^FK*%`_Hg#|1Tx4Thk`T+n<~hv9!tvT$@b%awPO+%+IY%Oa7@}GI{B{_wL)J zdrtOVJYC3>x09h~wMF6tM@g9`Ugue{fx8x)uj<~u*k-QFa zTfRT7FT7+MZrHAP;E(Hnh6%ja|8D%xaH(UpRsWsr%b(r-q|DZezy8m#Wb@%I@E^Ec-ku3pjS{N#*E$NQ5$nmk5QJ9e%;nfp-FGyY-u(;thk-Px73 zW~u6xU(%5$|1+3`)Lfli_xfe^>&1H)-?%mVxLEn*zl&a-ax;uOSAR#N`!d(EDr4=D(YxD*IqgcsgIDpXT~!enBxOm%RV*LMZ0rRPzsKUG2X2 zSnAEsR9Uy?+r?Mu`+K(Eh>5I}-?yXk&(1&hzXsg#7P)kI^XZ?PE+*K0Jy2=qJkRLq z7R}514@$WC|6J^QE->t6=J}bg{zZ|J5_ojg>v)3C<-gYf~ zcklbt&r?!GPmA(hoDv*mZN0Sieaf3}&%Dlu7cH(hxbJ#()r8jUpO5Bf)^zlQb6do` zkv^!s-CoV=@m62^yG6P`wN~w2pBh;7GyYco(`~yn^-8W+*`5BdZ0G(@^X^r@T+{z3 z)-1T3`{o_#=$FYmQp7t{JRcj)S+hS`D@iFWUgtHUk>#j}q^gNZf_+IA9Z{?GwKE8D}IxhDn+ulc)*R*o-PY8PXF68IzkNdPMnre^y z-skrA?)T)3V}6}GuJoqviFx{YZe-{mnYGWeUv3nO>6`B^75FoF*~|Y7Pptgkic2=ldiSl;3xMRYau(x?UUJ*4jr}=9XFc-S$`UOrY4a{Aao4P8q z`c~HZ>|1X$zVBEv=b={a%~LXK*NXebRNcG2_0qn*I>i@$tb1E(vDLb5(#zcEpD#(K zmWBS@o*Q)Ymcs*sI1cuFiIvOt&f2`YrjO6tv-)CTk$<#lUFzPs-7Ees&dr#!b7y(q zq-UOM^>&#qy?U)ZbJ34Yw|;%PW`F}UVe2Fr- zdS`u7uJe)X1zy{gyxXRIyXRf`usygbGWT&`#FNGaySKETYA0bTJdgPdi|^aB zzH#R#<*r_~T-@T*ws*_+&fW6z%+&XSw=-YNiFq`w*W#36?vgc+etz8_wtnmT)+O1~ zrtjJlzCEtMz(2q6+w9lZc1*L#?KauS{M7Zi$@2Uu3(YlSS8=2z27VV=y7bcQvZ(X3 zWG42nnzkZ@aeCbnm?hx=VK|-IKMm-R!>N)}zcxhDwgQ zZP$0_ZrVO|>k@9cgBl?Y47=`&JlP)HuJvQe+LQkC{abg{T;8+p(e3W?w32OIwIM&} zA9`GFc{}#H>Eevrb0gP1@4A3K{VIJc{@3DvAOCqfi0SWteD`1Xe};u?8H!C9UxD|c zJzf5X|35><^^I-+g#R;0|CzWh^*@7B#(#zzRsR{TRLX5)Etx+@sL|o1!;(M+5k{>p zw$6~GiD7cAijx>U*c-dpte1igL1mI+PkvP29abD!(f2g1e2UePkGmhM8A~--yb^47 z;}71hJ~=M7dgY%jU+k8%=O;~yHm{GK^hdExa*uo7X7yPIV;B9LYv!@Q?BnLPTaD+A zA2K*$nY&<>y%bM!R`<5)o9u2d?RBX++TPW;D2j2xGP4$?32hS{+5~;rX6boc(%2z5 zF-)-~K$};oF`}mjG!9^B{H$K49$srTi+*$m^+I3GIR7xA9@-0?2J3}3tk7(Q-vW9$fHoYKUsEt)tlWI}~n?>1r27vF4tHMSZEo5V1iG`y^_ee^c? z(5EG{N{>uj3H0|+`6H6(b%Qvw3Dy0|uKb+IOeikj;RHG&3g z*t-}?mDY$H->_wyXe-myL&udL=ZiT$xNG`8^{~&)zI!%8a!G3f40x8f8SZksvm?N1 z(wkLD#R1{<%BygY^)3{F8hu`@n{Gh&h+CBXr9``~f+;LYCJhE6#$BTKo<-^;j zr9=3?=1;22;oD`f>G{tpIXknp{~3z2qt5;PV%KrI>$Ocb_ob#u8$S9ZU0`(JRhlTa z;S`HW7kf63Qj?otS2aiG@&aY0?ykcg%M{yY$Q%85{Lg99-+0hiQjEp=F7RYc5OPm? z3#=!Ny2}}9)$^U+UPJVn7F8}$I|L=$U4>g#S%#K{N%dO&WNZv`U1N>8WPwZlN zL+ltn7X$9LY{ZZ3{|vhJ59)ur?X9u@F#pGY2ABL9^(SUQhmh7W#;QM8Ii;Z~)aX&B z8tAdaW3foX6fTAekuDjnJi`M4pdq4$1<4yd+>iWc5cv~b`A9Y@TeC}d+w8(Z7EXy{ zWhVkoyzxvc7hGS^#eQOy_q5Ne&Yp_dD}3SC98*;x55WpAq2HU^=>X=dP(q?0N5=Z(x}Gi;sPM;k5?c+G*GS z)!Ba%{?E|MAfLMRKg0a~KP~?m9y_q#%&mX^_&wEJJ`e|$go_TJ5(udgpXzg&0k)TwVze=O3z|2BU1^G|l&!e_sF z7XRj7S=WU;Cd0~L)p(;Iic4;lvBR6XoO9DDd5D^sfD5A?>|czrhMNX!1lD;h6SIwS+;6&})AAR}ZhVE1v|EWJzHi_aZ%pEcj; zx88q-y7rqA>pnkkIm%$xu;CX&ro;jrRmQ|60ZK-i4VOW)0tZtgPn7Im_kuZr{B1?cTZDFZUPUJoR_}wZ(7u z&evDXu8#9hKmR7H+Acry_x?vkw`coh%c*~?{w@96P5zDk*E;XY+k5{0E1&Uud+p-y zdkz1cD^GrQH^y}5Tlws~-1_Mczvq7Vvfp%H`n}}Z zvtTjka6Vp&x!U_bLrDES`;!?=N0E2Z+NM8T$b9DfZ{&iaB>6wX6JO{U=MAy#_BN`4 z9w|)|gt~l^k9xW^HBFNDNj}Of>!D-A#b(3hU@yY+!}?KLUx~k9+`&(8rUe(wE^L_m z?~5MesW&>o?Ce|;sa}(xU5VLTU-{)n<>$-YZ`OX7kxeRZJRde)ppQ@`B!%f=!&@x=OopJ@_D8O%jE z0v&kOO&Ho5q$(CT^D;?+N(g188EqFlE@=qdVorW0FY}+_L|FXy`QSo-2DH%kJNBP} zA2fbyk$=iv8!~}#sFDrm1VUz}{KqfzKj{BuT*1U`eSA~x%M&(6^IGbiUsPB3G%=?A z4yisMz^`jHan6(9bNFo8tUjNYU^u;iQ%HP*vC<@g)Q&gy=U;HX;AL=kkP6%X@pa*U zhMfIhj2$GmFj{V#nkddH;dCxAo;|aaai-(T9c)4F0UTc!zD)VG?D{Ofvwt7%eS7Kp zr@e2#-@JWs0{GBNzueS_X%7~2#Lg7`_-T5;(kXH+D^gX;cx+wyz16SARxWv4zu)2B zx0l&J*j6X*r#S4^+%uTFS&kiR$VW<%P;HO zPwvFuxg56=;wr2S7@Uhk*ZeVwJ?8lDKf}L^>!bPqsQn-Ob7;f~Xp8R?IjB=^G5vx1b>1ReoVZDE!Ud*bNuv)2~tgH84{*ztu%NX}fc4{1_ z&MZ8jFx8T=!KQgj+rbwFj$afmJEgzuYFNP7!0=esFm98b=L?BtiW&lK4I6H;s4|#b zVD@D&xu7^j$m9aE1|u(n`=uE(KlAt1Khf6zeI7JVvd+izoAAHV#6i2rN2!Z_POEb3fnhhO*;b;&PZRLWRB@UOp;!g#GL zzv8!WgWcz{z<=r+-hW|f;JbD2u5N(4!~yx4wkN-;|7TcZz#zhSK5JI^)R_;!53bZ3dGmMT7Fy}J zI^*`9@K3Mq&0f{NXYSOcfr)dcb-kM!dAfGm`@LJj(@Kv`T;|fd$}yMs*j=4i9qw;a zw^!EP$&HHb)tIAn{IiG=>)p=+;!l_A-~IgZ+l!;|aiKx8r_7uCRAlP=yz0COkKcdN z|Mj;z^UQOtg#HI8I&Wz>_ZpthR)H4+t9FYKAf<@Q!Z*X@0C<>SjREB|Tpll%VMd9~fl zCpGi4Ou!bcPWj0?2_BE!a^FaNFffpi$*B8&!zrrTt8mVe?R%CydV5hrWodTp`f6{f zYk%)=pHu(z&RU7RYfn8{fAzC{Y4*3+t>va)d9^NGx?2XIr;^R=?wu7;xgKNZ#|x zn0%Fmmho{}o!0lh^~p5{`ev*@^R@2l!sWNp0>3X7-L9i{b#3+@-_SGfS9@=|^f~;} ziD#wJx?0j^wMtj>qdxt6cJG!`p0`SpcA9IK?#0c!_N>`BGq6*>Qer~5+5DZXwaJ#| z%Z+=qPO80qxB8&EwXW%Qy`^eBiOZ)t++L*6sMOy?cn<)C|j&@Atz51Jr z=60TWrn@F`%8CvinH~XNQTwN#`{tcn66C#kwI8qb{U!IaP4>Kd_@6;<^7Xl)d;c@+ zT5Yj4yR@bHe)LbPf0sQkn;cel%(z$EyZe^0^!}||&&(D}pXkypv}mHGQ;1vH(Jgin zhT)!Gf)`gFpLSQ%;&bf%wc@EW;Q$ z*TCI|$bUFJvL4*mh>|~4|M)+{?ZC_Z{~0D4{b$(l6g+m`5VrjJlBUK*jvk8~9WJSe zh}dRGDlFh+RqPjG_~>xqp~LYmh7bAuyH4MH8FnitW&Q1CX*DH=w_B5X-=s`Zm-~3# zG(9!%&6f{_d+ywKUB3E1LqpBv81@~RnMSv7-*we}qW0{jtj|TJQ&Bd(2aGmNxM3OO zclX8}$)creudg)nUAB1fw)U4y2BkcE+9c(jPyE%s=JV4(_V^_yp%p9(*k-Y)GMF{; zEO5?X+`^Hk!PwRya6z%H!7l#e`9H0x{~0R(GknRJdb;z+;(x3^H-Fia|KWrEH|F*~ zONur?SD&c&5!vWdx%B1z)@|P(ncDnk*ivgZZ~6MflP4J7nCab}Azj#$+*5MQDp_^~ z;{=Ckr#C5XEuK?7Kd92aKZmcWL3IIBgQa>vXT>T5=4A{I`0q(EY+h0^@4fS;9a}b5 z<}TeejotXL@sU#-IvxEdvo_Ckv^QW*c<{XKc9vn3#wp*QcUAMJu2Y#bdD{1Wtxw;l ztp5@HEbZ``_~;$ev}Zn^n35SfEmE|lkD-#0hohWG^VaIC z-%i|{Uz>Ni{MFSr_h;?i{IWdh{~6uOZUKT0+YR{-l7jJdO8->g~xVoU6_=Bk@}ufBa+UY>WoYTx^y%~{{qE6iM{Zltp;wOD(a zuo-fGjAth@E4ygWAV9e?!lwQsAxKD`)!F=Ka$nckl0 zTGJM(6k~rD0aulxMg9tX#VVJ)A|FeK%<&LtyFX#4`W#l(Gw=S%MX1VkPmFqfy>^RC zyjK00r<#_fHK(-BzQ6eO+WY5C`R}~NjZW@3{$%prO-Wn=Zhpm2TNuk~?=H|YF!Zm! z|JHiHdC9xqyO(@@HTf=gcJ$J>Ppj|UU0+_EUw(J);~!srXGg1)d!=j!~kdcpGF`{LiN)HQjw zYto+Y`;S)E8;4)`+=Y2Y9lo<9zMgNhP5ov6@8bHfcJQ#tw<{%&|1&)C+yBP0{*_IC zg6P7ZKR!B!sfe6X5n)tVV&v#yrXs>qu^^*j0ecslP2dK8QFGb;nRYfWr-y&v#&%76 z=6{B=k8fS|1!r=cOok~8Qx0T`aO5(mT6d&cH?(y(9Clb^a+#aggd=kS+eC-{>3fR*Gbl#Z-vcc_ zDSA|=0a<^iE&a!a|8K*?{|uTkYaoMWoR4uXKUun@{`jT(1N*hT1iX|p z*Fh=-GUiz{>F@3H-!K1XaFb#x0{=?~P@jv8|qp&o2KT-^?{Gie_;Up`wib%u6 z3`vDYjSC+&PU2#ySde^FyWv5hv&6>$R%;C&$EStcB5zms`|v2uG1d!YS}dvcE{GDGyMM0zlPsP5kNrQxmh3AFp*_VJ_kRkn z|IG(lgLQ*7tA@9eU4&6%iQ#JpAucXC4Y4CljgxfM-WVQQl6+J_)@L=p2y5$Eg{}SP z@8*eL+v8o#@iXpy&{Q^mXWi2pEMFcg)QLVn9cdr@GBM+_|Can8F~7f^yQIybymfoL zo`kZ;;`n!qG(u&5X)xQq-kmG>dF{5_wcaKiQ4XrsHyA$pJ(6@iny~08@1iF+oSj75 zLfH=cD)O2fNCY(}+8X8ce?0%^wCO*?p8pKL%)AZ@e{}vM_~+&?H|rl2`o9?u{|mB= z0B;pqCwvUoP$k0xZh82mcR|MR#1e+K6MpA)^8$40N56p`91zS<;jLCS-a z2@EF{YBN_&n7k!b?6=x%Ez_62pAIcixp&e__xnwwxmhJx$3?LSvf(_hpz8i%y-oia z4*X~M)>}2DzU4o|@&63l7H^jR&!ANLpCR$|e}=#()eMie*l|n2Rol=K^!FqZt>|At|Oq0nEOP ziCc~sTl~SylaNy5TJsr!6f#^BNc;`yVWrQ~omI>WUQyuS`3=efFk5k!Rz!6kK^4m$_AR z)7@>Cyx(VhKdbK9?AG9zXM3~%UfI8o|C$%baQ)=kni?1L`KMlInftRDJuV-EOSSA> zFKKW38y~#sa*gqdS0~iAbW}xI zOcypDNeZ{ssNR3(R@cwiWxr$9*EMCuNnJ5%`J9)eEnsnPX{@`0@s>#oCNb!+`7_Vb zN_=%O=5pWMtF!)?{k)<5VD-c4wh>YGXJ%zTPkVbiIxFo~L`?joZ5Q_(b@Oo5arkPn zMv*h*l3?E5spq^(R)ysT^(?veXXWnNqh4o@JpGgYpFyGXKf~MU|N8p&^2mRD_@5!a z{-w@0qx0W)#ee(H@Nf3ShoWDXza(mCo|Qp_aqrUq46E$l%Rkw|nDqOf?fn<~e@R_n zH+GQQ`QiAQ{qFx6ZkfIc1kJw6|GU+9C;d<7^}p@w{xpj^5;;-xYk9z zN=Y2Yie4Pqa3ZN(+d#3O=l-5s@2bBfpY}7X-EaKrto@^T&1Q4+Yb(on_e|$vK4Nn$ z;~Ceo5Dmd4Ts=4VP6@VgZRYd$nq{*xVAkZW?y}bluV>6tzFgxj?Y;Dld&$NL@`*nu zY%6>3#(2Gq@6sve#Eo-gqB7r{N#%5Y6lVH#rtag+)YLwUE1P#uShZ@g&Y3d~41As{ z43;**U)8Q(lu>&b!%z4$lP73vU|SXfJYMj6C;Nt=65o z3@b)UzMNtpH(*W?zp{Kvd?a3&es=K<)5_O zdUC&3TIN@OxphCaa=v9(o0QD{;5o~yKNqX5-LAX$#QXKNyW|QV+y7+o2akQ1l&<6d z@!>zi_WfU`Ld+9&J_WulD?(|DtE~&noI8#Sq+SA3-^G~_uE||k~ghxZY!Oy5d z=KA%>TYt^=&%Rl;+m`?0yY+c{_nOyruiUKP^ff{!CoEZdv5b=%H*JvD3ABi?G#c@G$Lw+OjCvXHb`b~$KH?h@70b?;98efQp9 zQEg75-lm|k{|r1&**|>`&R_Z_VC#otYpc#p`&MpbqL(~DN6*W{YBBFlKC3Ce4-~m+ z>IO}^dGGw)r)DGO$w@VeKp@Z?)sN~({KICGnrkpy6$di`1ZAP-|pRNu(IRM#t-LKuXNGV zd8?=1@tj}A?^M}`8wy1tAC9CjA3V75_o`19qkesR|MHp2`$b=VzR!=&&b!t7pP{8V zgvt1ucTbGb68Dq!_l)(vM*NGPGv#MP%-kh%Ey3IN?9I8mXYogS*Q;-K^{3o-d2>JQ zwrAD0S>DxMHRh{yFKjTI&h&J8Mfa)Nre3N`9+B;p_v7lFE(J#A_r_y$BH8b9PsfrFa%J2lO4W~h+h%!p0Ax4Sy76rD3#u(NvOl>ZD9{qIKKo3-cqblo-Y zb4$Xjxa__x7Tq#O=U3E{DM^3c-Rq3K9iF-}*!7aGsnbUPifNNQ75Ypv(h9s2G}C%L zLUjr!?rh`K?n%26vsNNoeNlAw#rWO7cW-f?)cRCcY3l9!e!D8`564{UKjfJ&K6mBa zqbX94vLlX9*E#y6Va3$`DN9tcVjXg(=S)&oNLwVoC*-G{*2Lhhs_IR7TJIO?<{M1YPtB@wU0pUQsQcZzS@$m&3;))7-+$+n zv9OPw+kb|TC2fQIU1{F++MUs#g0}1bV?8Su_dV>6?xt6_y|0-+i(7T> z_0qYgtIFF=ZKI4$dzT(PlQ30Z#P9VE2Ct`%(i=Xcik=7Uax!T_+hJcO9ggp-m+t!@*JB&Yt7q-6B|tcAFuS!aB7EPG|Ylp%QUm9-{E z`KxWrPTbMdwSeuTd=0R zyF|O)F~{KZqE}u-R89JA_Vw~V&SPH4+CdjH`+o%IjvvYpp%?SHVj|L+cyYR^jctBH62G1?!W z8&xq&IX_>rB*n-jB~oOAf=%kotC7W1b=H|Pcuo9t@vVHKj!v-u%>BxTI@ah^DPHPr z*q*xL&dc@BZl2cNGvnBgxr=jBWb&N&j2MqqHB>eFT5~SdRhj(N#57(gtLo9QS-n@j zd-IiXrlcS9RQXhyJV(qjcY$8ns)l#>dS)ClxTdK;vC}bmVo!?Eq%}(f91pUTDD<^d zU3|g5{)hi#vyBTy+XK{tw#W!-*Zp-odFq^F`jh85yMHi>E|%#Pxl?jiruguosN;9* z+watRKAbO+IZv@qKv)Z;USc_$s8z$eR}SGd>PEy5r?N9sk-l&RhDfo z`Z^>@aRG}7!zqT$M~yrt3{$ucM>V*g{%850VZxgI@9qEfPTG9rJ^u&yul7#IYb2nZ z*4_MHU;p^e@Iq}zzsO4sroRl8m_rS+?#=d3^x6Mz{m*dGD4fmshwy)f?e$-lRQ#O( zd#V5L;{ObOF5h2Ao?UbCjHRK~4!Kv3|E7MLI9+GTbhr6;-qn@+#hoi$vAt)?zURdk z>Xf4POg>mIai95+fYql3ZRwKAjXNxEzi#ep+gno;Jtb>x(&Oy4+w(vA`#rz(T;$Gj ze?i&I9U7l?LoerMZ(9{rzFGdmw;Q*md)*R(I38*5X=0ST_e8Ee!GE@_k>&&C)90h2 zT-~m}y!-dsl{Jg_X0Ps=H=#$jeDn2LVgDKU_4X~Ulip!o+*Fs=A%F6i|1_%y^`AKA|GQcL;>GLT?0+iiZ{PprZJV?C@4M-L_y1>D_N4MyZ((NP(mOB9 z#Wj;vri*>&__XA#Ot+9vuXLrL&%?$kR;gxlWHwlwzhPTDcYDjTTkp>;m>jDw=e9N5 z^Tl_2_x|0QFPAKsR68fN+HTv__e;NaU3mE?)pX6Xe@EXbw^ih~9@1WO{9JI(#du{6@?F#GU)AT; znfJa~MDEdF6YZ3@Y~!9j*4Ss^+h!(nRT&mFI?d_xJQ}n2tm?9D*M0M|v#j9sPeP)uUM~ESwddL7Pg7FnUVU@P zx9rvRuq}HyYxmuaH061@=}!07NR_^Ow=`2qw|d6Bo2XbYX-)bwd*jrpZc8ooT(hRy z?p=H}cx_ls_fO09S}S+oG+8$%=j0{BDXD8M+?poGruxl(_EtO6?N-*dr&G_>>aY3o zdbQ>Hyh+l%f_l2kw+Nm)ud~|pbluZQ%8Z_SF04tJrett4$Z$^HQpqdJSFLt*(q0q2 z_|4weFH`R(?>&3Vl_~0rd+RfvY|EZ!s<-Rj>8WbdE?&D`AXpI}v7;r{Ik{4G zw`QSMw9mzJJ1{oQNtPKtV69qqUM(%ti3 zmz0GwzX>dkI=P2Q?^-2UFXV|%s9QcmsLA+x&sZpDXYH~DdO zUHfvo$m@FeoUh>*w=7w*S7=vt#_b>7{{3}xtuxzhuDK=Q(Mf}mo>=sT8f z3Ws-!cD-W}V~XE1_q*TZ@SDfvu0}@3rM}gFuU)-T+hkVu-NNkbD|@&56|_{&{jxbA za7*dt$zP9LH+`Y^CNJ0KblT+FrUSY{Z(I_$bv@Vi`_o%<9FVAXd8cWk5pjRFWY(hqHXJZ zjb>%n?XLWC`PnS%$YmG5?ybvSeY>^mc-F3Io(E=Lvbf^n6L3-|>kQxXPbayS=bs6= zsw?aH;`Yj*0KacFUsYA^?$X$DrzGFwZq^m+PnX+PU3q!TH|pcl<)Yg0-ghHygMO@4 zS@U(x*B80(^VMpdZsz6XhG}Qdc>6Bu(p~2jX2&>nTe{|Xu{h~I4Sn#uQb#dZIRJk;LQ>^}( zSw*Y1drzNccQ*cUV4jwqhp)NB!L1d$ChgicG3(TP@0};l&waB{Q!njG;Hk*0vT1cn z=I;wG-!?w0KJUfv^RtB?SG~Qv$GD|-!LkQllV+IqB%k(Ls;mEI)xqdYb51wi_|P+N z{=NeHY?p=}p>Y>x3baov0=?RfLlEB}zkvTNlnOQ-(5@nY}uiYHT_zF&Pb za_Q0~{~7lFz488Ze&zdj%a-n5lzs8#H_zP6l?RgxMW1Z;*ZFq&+;;l{R}=L&n>S5m z{4`00=V)V+|J}mOYV|G3(iV}$YESO0&6S#BsWfTQ=I-|eS^qBmI^}Ds{o=ab`BhnY zzWc&Ub-!#}Ty8eIULGhWiJVGWREH z31>fOnbmal)2dB{y>}OV*%F<<`%1Cjnruhwom!q}U&`gDn`%a8O`iH>Qgr6Bjx+l{ z>dmiS9+UYfz4N`wyf>a3w;kPAsPyu=;LlbWjhQz+JN%#a?~;A3arn+s-~A6)dG3Gy zW6IjiTlLBwp56Uz?#tWZ`gKcP|1;>zyuD|o?fHIfTwLVsj8FA-%4>CXa=*NekshFx2Cam%)= zFKheC()I9 zQP8|Q4@zv&AccX80{=51)+VAs%TZ(-iHpg@eR$aD9^t<%Jfv^#=BuFYjG1e)zL~W6M9?i%Bfo7N(f>Owe1X{zAkuskTA=*5paCzP3}{x5VFZ zQXXS1JC6fOmh!t-Z~(1V3K7VyO7&aSr4sK9twSqEJYkPO`J^XC-y_Y`<6Dvr=ZFuyEs+tdjKE!MwYd@6T($Se3Uj zdrkDxr#&kRS(>$86xkkpx5moIfTz8U;bSc4{g46+i`|})(>WJ3E^FA@CY2=AAbqJ} z!)1qvpUp_KY+)61AO7R`xBUL&e>~#QNzc3OzrOyd{~{dY-c>S%v5xHF#Jx40KX+bz z8E15*WJU`|P{ftQuu$DCRZMHv_8ky%kYQl064_XqvCcJZ+9|JZUpD)HwC$3dT4NGw zvE!7s%d*+q7yH^z&Q;xb#s85ziyVlN0+6h4fY)n;P;h+$%OYvr@Aq4>;`F@Pn{Pk8@pG2xtHkARHFkSl zyJ>Ry#pRa^^CPdn`+BME#X6y?G9MuUEWyU%l4!{p)AR;3d8qNqafvy%;#vC!b|d zg>N2UG~6-en1bFh)udOm`wpC8NaLQvmavMGSw(|=!QyK3i;#cXa!fzi88^@V(Qge*Mb3 zK5yIpx$Cok#ooR;;R4&){T*Aat&7w1%Zo)f^4Khy<=W3X_i#eOj-0uNC35CG>#RO? z>1x!qs+YGfpAg^mURL&f>9W0lYfhHz|BCIN2qxXiu8hT*%H|C+GAWh12fwX5x|IKS z=YNK6^S-X1`uabE#g(-mY=xXQDWR7F+OCfm*YF#XtV+2GW5TbhnN?7MGSxizUuOtt&)oW{@jj!kd1 znj5#I*LQqg-d=c3YTaG`+`g2#VjeT&%o(RnX$uQ7l8g}IP@dp;aBVH0c8RQyQ^^80 zDb~a-T*uGEA$6G-3-7UieWyP7{l@Ml{?E|8 z_?5`+)9>S-$lv&p_tpM`>VJk4b5cIDlSd0# zrUY&BPU%?H4P6gFkmBvNEA2AktNUnE%f6Q(V%OZ1CI{mSa`pG)s~DcYVB#03KOtKG z`b+(bwsre|7XD`_wEuSb-hYOf@qf?NzY{)}J>i2>gYZiSZXcExH%84E)V>Y|q_r%CxTZMbtm zOyGj&XL0b1?^W>9LW#K#|1-or{x!d{|A#^RH`ew)ONyt-zrOzQKf?vzLOIP#0j$3m zKHoSP%J7we|5AfI(P#CdPi%^>-S_g>e+EYfG5NnC^WXLV<<($TYf$*e{--hg-+uRh z9a*pcGfbENF8rS%*Y^F-ss9=7JpcF5=Fv`m77d1%4%`l`45|#ekOGMl%oWLD0+(1K z3;|3R7#kfpL2hF--E%%y$yZq7NpJJwm5NiRG_6@u#GxSI?xguf&i(2?mLEHF*R^cC z#uXJee`?C_4Sv-{6@~TtrsnkH z{-0dlUqOqJblg7tXINkO*MH~nKNj}CnLz6(P9uh-1i(X51z7ha5GTm`I)7K){tE%D z?f)4<)qgMk&*1FEaB=~Mjq`s7h0y;D>o5LiSR}XWKf}}Ge~_{+iN%hShggY?M{j&MUgI^z53F^ebi81MVG}E_QCy zVXZcIC$3HN9`xBCUifSmzumQqljdo59Y6EW;y=TLCHuoctAzqP?#X|BXTS2>&Gy61 z{~0#u*gsXCv*ABO-2VQ5P1zkE`6e~!UtrJt&%kkQ_J0ODP%`PQ?Z2|3Uj3i1{y&dD zn}0`3|7WnT|KwHwr(ynwzRa1&C(2blp8s+2s}TFi{~6S#|7TEJOPN`_FKGSKa={?r`_oZ{4{6 z^Y{M@Dj=t@|51+0|MPqK>whM-J)+P+4EqD?KZ-EaXP$pG z|NATbe@YGfZ!P}@|NHCupW#RVtLpEs*#D!8!G6p0ul~QkivMR2bYTBB=ikcz z40VhCGd$8@pr-};-=-cj{*+KSUrX<=ee;cfAq>1;4?dX5{bzW!=L#zi!n+rMfyon`#znPF?u+S4loCm6XeH(_+U_3GUx*EWG(L|d#G%Nzr0mv;jqW=(S{ySgJukwTC%y8C@j=Vr zr|bAG%9-nI?2|KWlPz!-wRKcxYfxUmVdIEYSV&A8{hCK*4=5#|L1GR{GAFi2ERAy zpL(n)XYD`bSjm;Y)$?OBX1$%?@iy$Y{{(O0f@&j+-zSy6x=XS&U-d}U=~+|i^Z8Cp z>@2(bkNf|$s(?4ut`>x}U)~h+x3m9eNC>gllz9v5_M~@!yFIo}`yK5CEV&;($qKsH zx9!@uqyHIRciY~W{voil@Xj&T%Hr?pLF-Q1Mb@_k^zHxl=1R(akCo-NyW;-I&6EFE z7qa&Be};HvDL#42Y<|7!nPR^VZrQdq;@PKf?&*7FABL7poMPyjG0Xj@d0xVc)!P+5 z99VIsu;lUC)ZZ&i?u2b>wOyQ-w)e@qrytd(O}9>O3)KprvatHJz+;V-+m`aBO_?>T z>HDNjPbZgOFFDKI8}{(w&hDz2I*aC?`x)@`%#7_iOFe!r3M(>f@_1!+Yo(@p*xvK$ zoEcl^Z+$ZPwtI%9>hj&8*7~LEG6GuCy~?som$UOmeXrCHRGa%{&XoN9yMEoN`8DTt z-L+?FJ+69>x^fFHbeOlg6kO+5wlm(6`}=^wfg@I%f@T#@`c`-Gl=jWtM;*`EOw2s> zXPr^*%}mu*TbGF_yl`98RCB@8bJebpQp@Nmi>_)MRe4)p-}bRQEjuf}VqM#dpPt_& zuU>t7_oDAHmHV4mU2pA{ZR8VaoOa`=*R!HMsrF4vZG&6Zo_jL&zP|3d>Auoi-;`=` zP41a0U%AFdCMN5s*~&PZl~;<&YcBe|di5>l)U);9<}{w@dw!-+YmxKs$@`S0=375? zc&_Dh`%1@;1F;LbR$iTwTxwtaeS81zMLlT`b=R-gQ7vEmd{fwz+~aSQ^Q)%oROUT@ zzuJG^omnT3M*e$uwzxF!*X&u^-l}tg{gr)HE-q=A>UuCGDAY--cjrmz?Pi`oH#XR+ ztXXrrF7N&#ue-Z$m)%=Fzw>Etgv^=OKwlDx?LTax$Wucl}cS|#t93h zy(Z4Rz2lbn+@qTt76^GB)AU@j_w9ar?b7|p9GaG@yx;d-+qQkb%l7@wa<|O4O-$Ky zep*Fy;oT?c4U^>F9cUGrRR4ThQTgeX;BLR_+WGH(+M93v7gzLVc5rvEe0Sx|=y`XO zcDdTdl>EysY1jOy|Ic>qe}>nq>tC2YNsIsV{@;C2eZKsJ{nP&pe-HgXyDw5weRWMC(aPtme5J&D_MHtla$8L28@SA|J6& z&n(sE`-D!goGq7@*UbCi*mK2amexB1`}j>5PdV~T7t#q>St-*R8aT=G1%vDh=B?98 zcDQZXUYZ*^J!Lyr#SWRaCiQbXM-t-#*l&GE4pVuzi{V+xg^)A+EbdMG`S*@Hh^=7a z7x>SBx5=zM;e+e)4XomfnvjLt5(`)tuz;F1kQKb}7CS4biFjd#lo?}j7n`aA&s7Dk zg#3!~BiH|l?G&%7Y2|bKan2`b#re}(Ow2sO%@a=eJxR?mn0;fqsA^hO*zUSpOD4t} zwPddlN?JT&nbSFkZ>$@%*X`SP_CLer9sic^pQ){*^tO57e}?TMI`-GsKmO0KVAf+B zc0o z?UD2NjJ#3XEtT8zZm5KDg(O}6wlUMv)AgK~U(EHaYt^Ux&Td`5bhqt1UH7Sp%g>&8 zc>c|*&qceJt^JVmwl4bi+OMYd+qc~R`Fq=chW#JwKZb7KzUWJ-;(vxl_dn>;)u|LPNTt2ej& z@16e)C!}L8%ZO&L4qB4XY~mtNEx3EB+`aCLSK9xv%712UIjUajwkiAH-1C3>+>buG z9^a<1cD>GoV^8~M7BnQRv$(x`{;55CqT19aPg<*b`fT-C-M6>?_Afm3G@S8m?KXLV zpFed^d+u+a^?kqoXYIU#_zzw6m;cTF&(LW9x3#i9^gqM$EA=1jf3RQt&yew-;fkLA ze+HKS4Brm^XW&@=*Z$>x(0ufbz_0PYYTf@cF#l(0kiTXBmAm^t!$JGM8|n{f{%7$2 zmH!W_5W;|Njh$?pNzyewF{j|Bs=5)Kwqse~g9|ndwUG%@^yrwms8kE$!XC>8-Bl zW4EK8?3r2%rk-kUKIUNSu=v&fFTeW#vHxdau-{VuN?iOuLsR|lq@(BNuZVL?+;L4e zt#{*;N&nn~n_$+k8I7iCYb$UIfx=6aExhik(LzgAC{a+YQs8WQJ! z=z9Dw{>y#P8rnA+zt;b?Tl}A)@jt@>{*}3!O>F6k--LKcK*>d@l)r+Km8`b%fh3spnf?kgDkjDj;;DKcHqijWM$Czf>mo@XZQd3zWG1HdhPnZEZ1Y}f0lzsYvz4R z|Ic8s{~Npee}+76*x3f?mfx($^K{Lj7GBhG#OH1DT&3!Ve5 zC&Lb~S}|xa-JLpRR?9pW3nKxql@@yL*1|U8`48i`<);=MVPXtuTF%~Np>ToGL2T05 zd$+tdgr@`@73M1!o0(RskSxK(es>awY5|`ogJ=fB0^S!krqzWhx4JYwP3TcfP%Tw* zidFu7rb}tEj=T)eFzZ(4f9#Ui%Y8(KXZ8B#kWRT%FyI>^rd zqwOzPe}4Ht_ig_f9$#TR78~uEYBKF|@1#R-rPn#$dT}%&)svlve#4q4i zYjYIodb3(-HJirGTL)yO<}NXlUU-n5y@{8hY}UQixn+j~yUv}9Sf*I;zDlKas+(e| z_Z5d#i@*CysrWQM4bi%|fL*z~V7i{1Mbb8gbyLNe&8~ zNAlK$rf@lG+}^fM1zA?MSn#`=u8EuXJn_?CBNI;5Bt zR8LArY|wsg#&^N*?SmZ*pKYT(c3*rdc%L;=B~XDWVySBMgd@k^_uh3(GUs!1H({I( zOC6x@a{&5?Cs>ee-pv0Dy7jN8|GV1NpnHKuj&M2*2c<($*SiZ;3a}n!P}5*61dncl zTfJu?iH;$I(XfjlCV;Vv!EXvfS_97orVEU|8H^rG4Yp~I|JlCz&+z)xebDxU>+%0$ z|1(_Q@o)Lwng1Ei=>KiJ_@Ci+6Kq1j`5MZb?CCAx__qMm5)5f@#6-kWB%IdNi zO!_d5r|RErf0DuEpvI9beaJ9O`*W_&!lsRJty8R9RZ`dz40k+XvPV_RcrT_wlyQTH zO2UJQ6F6m!j0&n#9wLj(V@L%p?@JH3b0G7gZG{kPY$(n3=lKR<`rSA2P zFVAILic~vhhAFzd42A>+{ZiudnWE$kvvD+P$JC2BDkrbz`|no z($vy*_v#w;*$t{00}SIs{+L&)m1)M|^;NX8U(1p}Utsp?J4+M3GB z1zO1wBW5e(5p7=gL_~!*C5P8}2-q=?vw zSLTIInQ-92#0icvA!>3n4^26pqs_@K%ASyYb2C)WaM2gWxnyUp>NZ_)E z(Lru2IrS3X_J`IhFRMAYJ>8Y$ULd8>VDA*V@r&IQ##bhcpPwJK6x~|UG{Gy{P5Q>q zN!$1r-a7D?!*r|KR1fn-LB03Sa=tk?>2c77fs z7V#4Bm2q=;Tll;EM8U$FJ6Ep1dq~^cs#iTPH8611iu0qI7Fl%=E=?7iawbcTrHDksfMjHuZdiq91xwPR2lF7DgLjT(S4Ql zH=agEQaCO#D!cz%uNrXqW_8!C%g=&hh1Q?BZgp{IHODE#>vxMLeSdcETDe#B?>}oz ztN*=pwa%MTySjT?Ur<-~p5*l7)>#G-OD<;?u3cm9t@N|Xp}1gj?5;!ml2`>#_3ew< zZ@TT1q`6Oktmxw3N2{x?YW7NhU(BiYbWJ*I<-XMOr}pnJ*0^_0wsx(1b*%o~%Y~Wx zonDKcT)e5hzDX?j)%9soX+PhF`x^V|P2K60IlVFb&1Kyi0TZ@x**pn0c9qGBSS`aI zvn$K)+FZ+e-m5FmeD$8>&3E<5-Agf7MQWdBdum7Cw)|Xe=^KAbE9mL9_j5y5=8E%bCWGED{Y%zV%{ke=Qmrq#rvlJ?P-}y%+Kvlb$&WSAtfaCq*1Gi ztE9y$y~Wu+Cd+GO)&9;&J)4^!Eh{+jfo?~Oip?&6;JA}hTouJ+Y*Rj=wiYW-RJOUb%jRyAARi=Ta`efpN` z-p3U?O*70u_+Zw&lj^oG%HOAo7;4fq13U9 z+LZ=BPYX}e7rG;{@507y-`NZ`oMfjKig|FM5}b!J_3(dVc;*Piz7T>s(t&(MzF zyDPU|yS}?-`;WZa`O|Lgw0&~>@Nett`uT?%_&0Cg{u6Y5=+NcrMY#lc`Pp9u7qTLZ z>>c$_xb}bjW&g$bdi=J+&+Wf+J{LVvp-3RnX&_g@;N-&}wc|fS zf3^LuPuoE==B3yFGfb}sO?JLdtN+aXpCO^7{@Wu7$ToZDgWzrUd`u1ccmFf2+Wwzm zo$h~z#m^UT=;%)S_U+cy(^~#Eg@(axT<=zSs0dErJmx6*CWHBM!iTud!j-2NubI^_ z@1CKyVTH}6<)a(wY|>?MwuEpkG%L?zbyK4(5AeBkTma zzvzJmb5hv$h}*vilrra<4 z`MOQXaRu4;hx&hVo&L`d*Z-ekal!&V^CR~^iS7N*uzux#hA+thtn0b`W%pcH3y(OQ z`TfMw85(-uE@^3;luS!vTJf}#fhlA9hgGV(zlFudZZt`G`%>igo5|G*eNz>F>1Wq# zUsyB!-YxIlnR(})rEQFOd+nylw~oIpi_iQjS|upe-M>Xke!JDSRAHS38*YGWKrqZ`vH}2bf;ntDJ4O^z&O^?>!vW07Ursc%v@do#F`s_mUEkmc=>epCT zSaZ(qKZED%)0d2v%ek$6DzeIS(W)o&kFMLeCB33_x$4@t=T--oec#sneU9b!C5xU{ zx`lm`+C(U_P>cWl$(Q1PJ+uFkQo^Xp;P^=XC)4`Bb^U(@zFz;&plAP1{ok$X`=7P{ zGweM7_fX~0A~_E19Y3tSL9ig>>u~vx@6-P?#D)E5_{F(y*MEjj@Bi&R{?+!)`G1M^ zZ}|HE-EoGT4SJ#ia=F-n0M_68zlQy1_fPKuZ;L%rcAWGwoNE`cZOYQKzMqzTojT>+o2{F!zNr%L+sB% z_;Ag4pNByL!CA*dHXC~dl$J2?OrBJ6+)yg+qPg4KW$R`I-uv~(e`Bi-`*mf_1esS} zd|H8X6#YFlf1muFsh%!vy7;Q;W}C+->0>@D>h(wCY^Wybv$@<4kF8!4e=t#L?TTGf zlY}nWXtwpKYDBB`9~V+^OI3Pl8Sv8tnOKQ1a5vc*>Hw zmRlB!>ng2MyPb79bnm_PWk0?jEVpBucG-W=B+Vsa9n7w~GedMG)T>sqc1_;S!q`9K z$)l{Ts+?}$yeEg|x7~X8{h9Icb91XIs;JgpUpq-!8uVzUIS{ z@C_%wRd-ID_sTrxaK6szsbUkSg&DrJD1Gomis9*=>;FF2r8TJb`0BlF?K`S-=SyJE zynT;Ws$I@bS@&mhmi=t+XD&+?=A~!v);IfA#}bzfx<=SID57%x-)Dc$GFa?c#i-hl zIB}Ajo5zW-k}N6ZEM?0sN3V_#eW7xBmw$TtlQ(ncr>D(d>|6u3X~M|`k?&&M`MwuV z5-sv)n0(+1!{j$wi^bJ5mffE?=ed;kyv2cC+t%L9db97_8>>g!vz^2WRZ@ByZnwF4 zoH!pi!OgwEJ%z9{KKxK^ z{aY*M&zyPZ?);hapMiaMUH0vj=dY%`c-gR-^F6pTKom+h?15K@0uDhn>#W`o+B{#l1PkI`R`*uja z(7JPHy7t!fwaTJff2-b|edWlN)$*x&uF@+_xk_H%U8*v>pe1_Kl(n^{X`*RG&_Od) zN-XES+T~^5v%@o%bv7%n=jpLBns=m>rHEg7){2uy@8+*}`5snfYi71QC?sW7@TN(= zS&LOOU5|Ml^E|p-?6R-z>0R6QuE;*O=~cMU-IG#p!=`p`2rAwpe9m}s&jTTkSDB~s z!u+m<-&EbZepBqk((H4adeToV7P}SLqPhC?RPzis&25WiW4{?g?nYbi4Az4*sk@x_ z@z`W{WfQLek*Og9vG*5t{``63_uUnYzp|ZfZJYmO_3~damiJ!${QIQ>(O-}bi(|j? zb*>(FznN37MZ|-$q>TsOZaK5cPyE@>jsRo%^OGH9?|1JpmR)qXI%9XxwPT-d9{A5N zFX`ChQ^{X)R@!=&F5s8@@XJZU=9tjDBJCyf(__@bYA>%gj7bsgVe+$N&#f{MiSo)b zn=&i&lU``Xt2=w2&AmG*>zwYr=e@nfn|n=7^*k+3$zA<@S2-(J<$ZeP%j_4|4fEK& zJt7uvGweF1)5&(AhkFy#K^N|t0-TXfaJ@)n=Xl(o{wK22Kb7Thy!Q`P6Y zrg0AxXjo&vg*ko{W?@u$hU(NDp zrI*aduqMAnLH-ke@7c2MeYVPd>+ey4yS=V$sykPF@zm1kn`h98k&TN5a%buF8`n>bfoHeVqJbLWvcQVdAD9BHJgr7RD8nZ?c;Afc)<1!8`r#7+5U(J10izXJyQS<$2-nR+Ut)xq9pS&ad59 zKWDEt>slGMc=~&hpp_cS&$FIc%IBBzv*g~zX}xhT7rj26EAqUcGdT4Iuk>xbz_m|8 z9h6Rw>vp4sItz36@->MCB-U&z2$NA z${D8n0&7wW1>J)4WDDk-J!n6ltCDvt!nQp#s2r7`G!8N`!=oU@2zcf_RL=XsyOseHMn<(y+DMNl8noj)t`?4 z_4Gf()}#g8CHR!$k@E%jEln7~$8>_$7BU^ynYLZv?%^$aw{&Z@pSyJ8)F}=LmDI|E z?6MpS_@@68mUaKV`aeTs)%^bq>H#dp725-5X)0>Vsr7lxHW&ZIut`~2cGBIGk_v{} zJ0;i-Co2T7i+)r88h;%$dMNyBJ$YBqv%XN>warl`r$JNIGc?Vqa8loiSk43X%KA26 z*VZnXQYn1ttgh&#@?AmOjr`ubyj}h7T;_%4K{22F-l|+TnKUW7*ws`r|J&rtzFqc7 z^`U?34{fq?e&hdY|9a4Aq7=K1k#Y~>He!Er-bGH&?e-s3w@iaGEKm_HGu0d{{J@t7HUrg diff --git a/doc/qtdesignstudio/examples/doc/images/loginui1-project.png b/doc/qtdesignstudio/examples/doc/images/loginui1-project.png index 93ca7c6c8c9f4fb0eb49f5a579fd7e9d101d73a1..28984175b94f76dcb086e20f7ea2480253d986d3 100644 GIT binary patch literal 35138 zcmeAS@N?(olHy`uVBq!ia0y~yU~*<)V2b2mV_;xdRbrUNz`(#+;1OBOz`%C|gc+x5 z^GP!>FtC?+`ns||fkrqeA!W?js6a zoT(4qZ8zD#=dvS)l{39xG9%w z+5dzVK^N$HbtR=)XxN%j9e&%c+~bUu1ZhTyk1Hy^)E+aj&(-uLA9%chCS z?vJ)!kNaF^9K~Xv?DEg~v71X@#^S^k0^RNW#|s`SbG}|3yJA_D z?W7pnYs_iq=k?B+BNGx97Ih~+GP1K*+FVIh_2^M!6$M*;{>qn}V%yRa!&giA|2B@7Z8j{aq4<2j(*Z_{+<%$cw}Nz>3BJ3fA%o$`ml>~GBMcg4P^ z>yLG;u-zcprsA3Pt)P7VbC<+>$CN}wMH3&}C=_3;viO^BTq3opYm}g#mc#A*v)@|R{4m(GdGp6R#pfUHdcCgruZl%mvZDTiNV%fWpIRzM z)f1<_)xT}FL-5F(9VIV?jEsy7N?(PzxXhTZprpjqZ~IN+UhVhTKfhkDpJ#7rxvt7q z>+zHGd4U&&c+SM6e6q~?6Lb3avVBXo2P7ErMIOZmszL_xr~y!TyP_udTJH`l4~@@L|Ku zODcb!>eoB^`u1+0?Xhs#{L9jY3a6qD21e{ClHhFgpv2QhFQXnCySe1fzFM;A z^2;@O<)!hjCsgN%>H8h}@UT0O<3;;nsonRa_h*>Bylbev+&=7H<5$mR$HkLAeqcS} zs%f;G=X!l{Zq%`lV&(7dNESak?>?sra_NFN5` z|K1$}E`n*4yXBTnX{1-8I*(+ZnbTO7H2oWzwDK!I5aXXUDGfs-f%Sd+)3$ z+;RB_`&JQmwmof&7PU&WI_b=t)Y<6$CHB4a^M@yxybnjWM%bjPACuhmVmia>>*3CP zmcbi}@3_@ouN4&QlWJ{s^vjeJXsdK7%4<0-yVVuSjwl=Eu$B&Bs z`slk8zw!0?)*gNI;Y#0@iX+F5E8G2(eIq-uBU12b#MHgw%I|V~oH@(;4%~aQ%+{OJ zBi>H*_`Qk!^~}nKhtl?1PZ0ah_wA8{*^(p5cDe4GtsVuNDLi4loVZ1xC1ii&kBaAN zw%4y+OL~58?hNB}zI{I)aew3Zx#G}K?t+wiCDW^G=Ulu5N`|}Fv?(gC(f4`#%I^H7 z@27a=`!r`VZ&b;alG-2RmN-XIGBo@V$CeJ`KU;a{wx#51YSm>0#g?k}&A4J&zwwbcKH%5!)A^EEJS#k;%Rjw@*vD`t8-*AuVi* zH@fe(?K&NUtXdsZFm!#8 z60@}R35$!%d$aBR&CSabGLe%4cXV$I7XS)Zn5t?`dy*%Ovx z&k_3mm|yha48E+b=OqNUdWNY7Xp3;!{QL3vSi|A~jRj$=1w}+yJSWY$_k63~%L@iP zE^42+#gndRFkF4T(JH+AN$a%JYvb%?7yJBoXusE47$~+a+WPJmog5b-vCuH)#c!ss zU$f>&8?UrM`Ma3M)gm%7Jx`vb)OtRrl?1DI6V!u5c|ZmOnr^hEk1>$FeNvJ zve&s`?8_dVV@Wv9#~`?Qf<==4rq~#+69qqO+}zptDPqMu5Z-dm6 z{MGXHe+E1fL3j$a^_&mh9^djKLdi^!Y2-w>+AW$?x|w z{6@2&L}8BktiA~^`Q2_=R~tTJt$g%Z`^IGppR}#_WN+-fUc&U1Z^_aehmL)Hd!8z9 zR98KF>SvC$O@)D6{hz|_>F4A3)!eMOxp!}?Kubi#L>9uTo@@l9KvxkX_!yT0vEnb&`tZ-t!l|U%!6!>ciLT@z%HM%tezv zeyBN8nKR?8Wn^UJ44cYLXHEU|Vt2Kioo$|ctVc5KcI_v%NrIavY_$=&`EgyVWVx!k zdh+9Ay=K=1UtL)kI+@tLB;_1R3%MLK%<#dep3uxmv=yoNUS zyUQQl{(q>?#pTMr2wvxX^_hF6ot7)E&X~15`Xb+y&oan&#SZ zY!%*T{i{&;DzOaA{2S(wOQs{hMV~W^HV&Y}MCykI!W*Eeb0t4pCXZLurxO z`Da&Cw8PisY|~d&J-W13xAJ;bdd=o{Zj~1P|LlJ*Ppmlqq+GG#)2XO4|1Mju-#WD; z>iE%a@#ro8*2V7Lmhso+iQH=86|Y~v)|Gp3F7|b;A`hqHg{}V&Pgp!bWx1Tkr{cta z#S>0^O$+Yq=vX1T<)QS}T_-Ncrl@nA{JrC8J$?oAVp=(xDZnb-ZP zsQLHvd0lZ_mbC~MtBDk^TaQHJj2RM-T?6d@ei7#4;tGh0dUY+TNjG-C^&CFu+<6Vo zjvn@nCmOl7p3te2-8eP-bJbvYpK)z zy1xSQ^8Fnh9B*!HWS*`c|83{5yQ$wVRb95{@YSDRo;H7dOq9M1uhRsX(@wdE1kT)_ z*1NY}G2Q<)^YuW{G8W=5D`RCY`tQsaVdP9UCKO*Z+JfE@NLOGg-~o zDJMtA?*E_S<@B|Ssme6c!GVE^iRsKXX}f~9zvtc*f9F3kV~53&-p+3Ls+ZKKlJH}_3?^!LOSGr7r=t(JWJ zk$ZbWvc`5@(TN#B=a%eCD`hz0E8F(s%G-5&*_6y#SXmb)AMXp3m$fQ!aB*R=DtXbs zCu?Q#m%r+GpX|k*#p$d2`(&+^)YRByt;<}N`^}xR&R#)*!O_t%Oun(PamUwd(Si~Z z9S06L^#55~{pqB7uf3}0rk|I$*!(m*-0JyFFQDkyrqt7C>UO=F7M&vU9t=)V3 zN|vcy-H*gCudbfn@%GBf;2-bn|93~{@4fo=mu}(KI{yv7rXLi@^O1k|t~mU%!;_l+ z2@j>`|5Z>~QpFPUUE1evtwi~K+1EA6Uq6=xe9&5y-1czd8m$)9RO!Q~?oT#ZamLG~ zZUzuDg%=C?Qb7qhcy>Dl?qo}MhWDu0*r=Y8g{-TIkQ z4KchfT&+xdtG*uk`1p8Wd_4bDuh!Z5`y@e$c2~*EriIS!kG|c`cXx1LXc4d|dn57d z>+9!bf0f;O4D9Z#ydQk)4olYM507&Mm>61D8DA)r&@+kYKh5j)<)sYE)<~5p|J~QG z-p$fF;eQ1i8=FbszbalbUN3MVVecyi^#QyW6aQ;k%eBB>AtWuVR zXcc~a70RI~ARy52;X}cfmzUXNca*Md&(_7>)QRz>Hd%ZwY9Nr zfAzh^OuUWZy3+*5g87<1F5i=?`IBsEwJOx2Zl#$0|2myte}8&^c`MW6c-Q*Thn-S3 z1~07s80uZBYjt@tY5o3xQoGCE8l8I?xcX|qYTNJXFDLuk9ej3ncJKXH%uGx%zmk&A zF7uteA@ML<^6$Aj-%ik#ulu1muj-X1!zY6UOTRB!xbWbbKgQ>69)GL!EPHr}RVR8I z&v~2Apq6^-pW3stOfBmE?ASh=ox#J;Z`&??3yT{oceMzBvh|`xixvc}36bY12;FP-(7cKD*g3$k^Qp^m%mzY@YbaH8(wz`{>b~LvAOmp z|Ivi={^Aq<2AEv?SHQ;A`a0kLonPe}Z{c4kaqRIgN?2u4Q6 zfR!O~bB(VAJw0hQ)vI-~y8k0k;JmrHx$xB$&6-aq)yF%@xv_D zc^&%%H%8unbjtMbBfn4o1=!2qbIcWxxU*OIRjFT}VEfqv_q)>>8tQ82|LiyxG-XNp zVinF~Z$B3?CMb!uy+8g)r7}J>Rg^*fu+Ka86SE|{m)y7>81!dXcB#NtC1?IF0j8+6 zVwIJZD^{;&R`B2A=iyP1Epa+v|HJRQ-|gc5ey>`e;X$|lzJh9wb6fs$i|J&1l@h%WR$drlp^s@SH5}&ObHx zm+is#hb~;5Z=Uq9zS%&FFpO_^JRR- zdNmR=ck$NyZz}2e`7u>eSa9yeA2lzJBt~RyEc1SDo3hv@@bn}V&c*KiZi$JC3=ck^ zw?F>A?z{DqCr=zW8aaj45>8Lk&AVEEzxMmX&*$yiofbBzcpf`>mf^!O>HG()*Y7L( zDQ|RGX5rfC?M@ttg>lUfrT3{n=aHPIk#VX-`Z-T!*!6X>hSlG4bmI5PfI7_G`ujSb zo}T{rNT;xz?6Fe!q9UWP^>Mj{yS4W^Yfa_Sn#z@!nCRi-bL89G+n^LB}b^u#L#k~?Br7!(`ceR|@~(KuiHM7G4=JrD92 z9G+f2%)sED-uCF@PVJ}-u2D8~moREwPhy?kx~$BDqj93Ld(x8=6L0LTHV@Gfef9eF z#>~rVHh(@GW_Z$h{Oi}RdTXa9@5p)sikkHKwQN(pSQS~An3;Ra7xVM*l>F>cI2@Wh zS6vF+o%H?PU67OXVs||W{#xDj{?GMX zU8e^=t$UvNsmIEPEOUAM>Zg3+$AY$+qqYnFYc6PDnx0ki<9)rY?M&}_ZvMl;{&!C` zpI~ooX-W9<;$m8{GEepd$$6_ow5GrFRB1V|z_Ga}yx5f2=KuHl|Ifp2w+cuYrEsYE z&wCSQd#>WsNp(RHk(MPYJ9po>)^+*d!GmukKN%cWiFf{C`1kJQJ8vCLcwOGHEHq3$ zrl6+AwmNL>p;m73fWW}ULx-G7USH##YgKya>gw>qe}5{cultns@|W$&_lGXruvGV* z_jmpDsKn%q-W79Ku3B~I++1tJvNsWrw;ed(;4{bKVuk6oDVo6_PO8seux;D5m%sjh zp8r4L@v+_+MyXz#YyE9LvM@YYU;lUY>9_jY+J}$J*Y~)~*Is#QHRt2QcKJsym(O<# z3!CPw9k4n5#OGP-=00D|0}Ahw%f99h7x&v`ne{*a^YgPs{l7h@bF*LmvOW9$kbvM; zzQguPT2s0H|GK`vN85y3al(WN4ZgF@B%ib8+}k7h_;|m2Uf#Ok|35TDxK@N{J$d>x za!=HnSWwNK>j z|69f*XCo24J@4qr$?9+AA0&URp5?Uo;*TqrW|fwfzPP%(fBWpj>)!hN7(6*RJTLjL z5D{=P%A4Hc@ONwWjV+bj=jK{JfBQ^mYsc)xN*mM9&r7?l>giNlYsDCgx3S6ky`z6tkyaFMoUM>W|+)K0f~V^ZESSwY~G6t^GVH|JC*N@~>XKipuewKW*Lp%QuXl zD0y<&%$ku=qu2A_e_P^VdjVl#=D8|~udn6uxI77;;+492p2+1&@o9O_-kf=oxil!# z%zs(tU*GT6*2|Z@^bMbVFY3fxOO<~DIjdtP|? zB!8>bTt41k>6?mVw#T58wwXlKE-u&O^ie>kNyr^RG*Ti`~I}2uc>6*CA0swr7Jh~&AFoZ zi)$92XQaisnG02`-(EC(cmB$$@2T1M)Pm3UXW#mErYE=8xjgLtYPI!8*UWjj;l99` z`)O5s61NucPTKT+vC869c8<=D6<;HHoZC#TPi%@jy4;}b^md6~_uM=twKRI0Cp@^s zD=}rGpy15e3=Fc?EQvX9-tPF^cl*QN`t=X~L`IueZ*}cH^G0%omvq~U8HpW!G2Fhp zJP#P${BHC}D*xlf9yZU1@uy0ch$`r0`f;E6z45vn@1!&){nxhzX77{Ff0)XCE2Svr zQIx_<-N_Bh8lSy5-O)F*KW6s((931V6zv1!^>{t}WSI^JB*fG$+GCq0@$Bidq_{Jy zH5^1Y3AHO)P1SA}7Tjtcc6h>SbJH*ER*Dnm3iNuHemzzh`hV_s0fq+`Vt&fG@mk4d zWv@@X@*!GKU_oBl-kFD$4li_M{KWI0ZF*`#X(QXi<}bf}@;*dO+j#6)%{;A?+6DSE zeyef`v^CC~=aaMDHuS`==6`ZIJL4BIgr2xxc%sp4rcC;;UtfRsl-{UXKWp3N3zviU z$g95G$idyup!D7`CW0|6U8d-Du7c8{ZLQOWnFN}e<8;-43b`;%^M z$t;s!Tk7nb|1%-OKSx@gd8relr~1PS#*-Nt-^~8IU}N~(PgA2}Rfl!{bpcD>)8XBqYkf|Y1Vdfjpp%-UX$+$zl~6TZ7{R@ z@x*TtiyUK`>gG+@_g(G$rHrX-zn^m2`D9t~lvCT3_g^|xrlgd0ea=8Bg5ZCUDsLJm;^4oREb%;wpqnf<4$x_>u33+;qf^I z3(6{AsCoWh10IqnGSoMevAp>^a1tLw=n1bmE8br`adgj?jT)ZQzlWx+b!B`sr^dB? zLy2$WMm~`p#V_}$-%9orU3KWbljp`esv#51c&4SlyHT}XXX#|-uyjriN3pw%iH#&(p$YIwcNTDwI*`&v7FyqGB3Z0lhePy zB_vC1&a*Q!FTbgCnYLxB^j0G8+I_yah2PxW^-+SR zc)Zf0ZD&``Tln?$_3a_nw`3~!O=yT(o3-uz|9_v97#^Hsf951B(D487XQPtBcXku? zE}om&xZ%`Ri#wl|XH{=W7L1N;6up;7PWq!}!0Gv5Z|Z3UmBcr^ z!cGoCLMU)%cnoRzMe}qts<_W)$rHqbsl0fyXL%+ z+91iruTF)s&U^CJP4sez0+#;^nQKEdq0Ld|g~za+#j_MC zT;CTLG$_6?;dSw2S7c;wo?zT~qQ*7zr`ke;NY|Yw%MRD>-lR2gipqWYxP3dbS&X{l z-E|rwF3;|sbv3x5)$sGg*EzB-E-r6-jv6cp&=3$1cyOWXVqbvP)G+;%-`kcop7<%J zc-UYjc({Utv4o3l-IZIb0{NT@e|C19vq;$#3oA4^k0os3{md_&7qiMS=!cZc@iXjO zyaOlMvESTTtng2x^%=5H83tY(jT%Xj#~49&IEZ#lWR zJdv(2ow>T@uL8@7)kUkXIQDLTwP9I~^(XD*iF2%$ocy+Ug_F)B-kGXKq9-@1*}C>! zx_)_0osyE0{c(dQN;{KJlnFFVkW!DjJ=eLG!&`k{*QzBsl@hBf?2WcEOz5uc%wFhu zoj*VIxT5{W%=dk&J5Oj|;O|b&=>2+NNnPb`<5B@Z!L9aOy>5?#uh(6_dUav=d%Jsi zZLzz7(7|D zvDmL_H>bSA{Yq$P=sgWhO=H+vfV}3JcC|J2`ve5H@~|o1 zz83GDsGyX^_(t;6rs;7tjFSWeCb1~8sCYVXoZR6n#Ps$0y2Q)RbS=u?&DlQN+r{O| zI|H7|nJO%DzCI>)=IfrW*pA-ZGZ&eAC4lEe&h|MLVa$aOqqMhlbllQzQ`8s!CH?S~ z%1Midw=5W=*Sq)2Ntl=Ew5U8hyk=`$rg~s@FwYB~uYVK;M5muEf5EWgOX{yum&4Dp zI%4M>c6V8qEb+G9@pmW5^%hTjyk%7PnMb)Z&d)wHN%+Xp zY=P;o6s1)1&5!!-6_`*g9m2f#E}v4;`3ciG6;0dbt2wTCS?p=?@wmN_=*d}3A_;2G z+@B?BigPaewrH``s@+x2-@j^h{F=t&vR7cj;>IlxtY7qPd5}5hi1xbOibR>z&g^Az=seHP?NwfOAr=DFOJ_j~^TgCB0^AHN=7-&>z^BVqA1#U1K= zC;lH+oT4aS@mbC#O{VFNNwbi3o-c)OseD6TX8O+pPtC+`OQUoBEvZ^irc z|88lq+A;l?M~b$`Eh#hiOGC&^p&$dh@~s%Nj+cJ|KCseg)` zpG?>-qkl`u)_E>dqxs{;4(X^x-@dsTsmLaOpCD7`(|(S}WL5xlOg4M>r4!Wx?KL_2 z%mLeEe|8+UP5WGA;xUVJkBl+T#m?-;CEIlazclia#XuIBRX`XO_ zo6)<EXi1;wo?hC`E`CpFv zX|$H?K9;i2Uwwu1xfic(nU6j(_$z-sjw3>dZDUBhepS@Z#o23fU9L#k$hBFqGUcl> z?0?Ju=e;eD%QqG_7N>;_>#n>mc;zK4qGig&>?7C6$icrYJh4r^!1+)TG{v@n^Py-_K=E^69FHr6y&pI0aT3bC$m z`o~x6AMkFvhJyrK(Ipd0hl|P*r*7vNR5MK2Z{qQ3JDcnOJ_m8rFR@y_F^%t}o%wy; zH7;b{vVaPar^PC+Z`)HD@taZ5opPKb@9iaw=55q$~ydv^q;r#Z%5xg z(R{Ht$B(N%eIc>n*vIbhf;UT#3x4|M{^IshWwx^YeD3G|cCjyJQ^*X^XV1Fw=%otp zp$WZ{R4%_wUg)YCId2S2i7a8TF#;oXLGXmLof}1>E%=SBPAAYsXu- z>)gR8T^@U0p5iwb8WTC@@L%!&!6jtnxYg#=!D6dZzb;4!Zq@1k#?xBP%vNH2|C%L7 zgb4cz%h214-kINN*!r6<@%iK^5nJ1vIro39eEcEztLK{eR$}u1cCWdqRBR zZ~kv&Umz>qed-T?LY?Yg#hml&58XeWsaVb%x=`zn?i7*JckNg+5BpU0U;jUU zk5RGOJbBq(hp9q}`SmHvpHEbuoAdK%R_C1%?}w}!r)P9zFkJADwfl9@<&5{%nD(c& zJ7Z}XYYMeW-2&*YW538%2T zR$;Ew(&W^I^9^!8#Z3PsT2j`Z`^o0N!mBUEZ3YHBjgzw#`XV&ax8}NBxpariIkxGE z=OyOdr_aCVV_)~O!Cw57t;<$k9;a_HYuC2=OIco?fr&h=2Wd`e(sY?uQmw#J#*R!CY{w)bzsvmlaYb2ryoG+3@|_ zuG=R66;v9(Pt$Flz~a^`)0w~g=)xxl7;F9=6&IM)pu*BJLBW&Bi6iIkrlYrg)s)(2 zu8&vopOhw{^vRUV!bJG!oQGN)58BRpzj`gZSRJQK!AG@AwZFbNuD@3sV?CoI>gw6R zi53yBc2@8G5GMSs@S>~I@jI2%_qEvbJfjyvvnYGJMWF zPP)zchoH^mEU)&I4f^6YKF43XA~>!2!qut={FOXT z^Acw0=-OvqvR+cZE+qm-0B`G@@v4b^J6 z?KCTY{aW@*`&jrsO|z6soMUj;gu_vyd4ktcm#=+GS6Kg*XA$`;`gm9A*0_hA$}1#| z1RC3(*UCG8zrV%f^4r_rRnMvSxTZ54$hc!5yyHi$X+uCs{bzO;Tia{iyvj;SSt+JG zckg)1sTm(#Xn0=um**wlW`P!&mL=Nhg(aW=JHNMDIgW zV)^Uz7f}`aJs&VWQ7Ez3>-*&+XLtJ-^Dn9Dp02?1$MXfYmI_)umY(Brvv1w$uZMFo z59rSiV-DMX`uO}mnQQ8PJFkeG{B&uh*Zl`Br*3n2O}>4D|90WMoO(mQJeFTCZ&%vY zYSbN@U$fw@i^~(=Fn#~7m)viDOWyczyx~1dK;1Ue*-!0T66c&rnQ`WVa>&N7p)tEF zq!?qX9v%Bpc_VTMzc~BdR}O1G8H9Wnu6Is76)?x+H)HMZy58<~Ij4SeXWc3Nj@R$M z+CG1Ui)>e+U8+XUPV>YyU*Gyzs^RK98P4BjN9(H``}Iw7ni!DSrS$MxPS6}xw5HeweRcs z((T8#=^fb`@2|AL+rPy>il_AXyd70vR~1kFUd!`0E${flMfn%X7a6nLwOm-+{iV3} zi(g38y}tZ4*Mx553uJXYv2xBtPS0v+3@6(^nQyFQoEMKCDZ*TABUkeXr&tJEth(}N`P>=c!Iv zGV+(N&T(#SRZ#iDs=~fWpia1Lm#1@JR>iyxOo99-7?=E>tsP+gvTn+{iRGo<#&gY6 z45VtO?{7IZoVKrDQO9>)M6SjB$I}nDFZw;DJz$ny2%iZjzh!~s zzl<-w**41_pP7Dv_qMF9qM+bUO;#Pe#gY%;(q*w?&*1o`jgxDv4m);u8Xy5&YPHgp}Iop%G}xO%QP*GeB4}IT>KIx zJ{c*p6xsjg37j}_MQ;U*{aowr_n-HJ7TE|m$w>)Ml4+UI+R@Qr)0cSS^u9fr5)2CQ z3>@zmzPx7p@;Ptzt^SKIc9(A1F74>zVs}pc@PuFe{nfiUU7mET@W|iZ&~YNyPO|M* zQ~tHP8t1KwM<^&3dcnTek@b3QjD|>UDek)@;Vg)vG_33Ck$8 zI9&h7@7(7!sZKM~vrg|!J)h9ddpTO5E^R{TMb}M-D|T1jcJ#RSes3B`doGW&-X;C! z7Y+iGz>ck%aldV$VPWCBP=yBp0!DW_@2xu#@YN=Xb|X$Zk=o!gE`a0 zy3~x{jwdcUq}(!I{6BVf*PKUxH8k(_`{)YH+{LT<>$&Ah=QI7rnzwiG|9ZIn@3F^5 zTc15Yedb2nq(@&*=+((?yr7_2ZSQ5Yg`q9spPBb@K|#T-`gQVcF{wM2&)5rEQ34vG zDf`AFrE-=fu1~V<1zIm;!PXG7Lfr{p!je~m1^N<5|HHM z<(iPrVYKY`m*bMJb6yp&Y9+^;zfT1%CTow0YK)qs!Xs-X!pX_W!O3}WWAgD2N5$hG zJUG}Kpmp0XeX_Nq$BebcDV7|L9y`LS<;?F`2wmi@X87}J;k4UEEX=kW`_dxW)Q+UQ zXn$s+;gS3vR8aW)sf&9~D)|5Rd-AzCmK^;2?gT2ae13K><(+q2}_#_ZJiI}3J-e+ye^ zSGk3u_E2)+rwqn-mETIZx43klc{I~v){C>IT_@u2%7}J0FP-;W{@$-_O@@Zg+Bbzg zUEb~dylyuG!^Mk+Wv8RwN(wnCXlZc?tN9#=|My88G*?^v{9LO5Q}ni+gR9r?>k3@# zrl_dMSXo)Ap`+sx6C)E86to~fUo52ujRiAs(O|3xe+Q_S_QzSwg$ zNJ&X))4{a!6Py+vn5G*IT3TUOd!~JzlatejL)`ia&(6%O`1kYqY*~33nV!qb{gYo` zTg$=4l@-3%iNT=koM*ndqrUj(q>7p1f`SuQ=fwFg3(!~)piyv`*E~X3*>lp>THESx zEm3Rpf~VgW78IOVzwwrF3Ww8N>vFgKb${1{CA^;WabNB4Mc&i(R;*oHx_+wNi>2c6 zpvHCGK~GO1tdq8af)ho})IWJlYMGt4>*QKt9w`$JRaMmht*LLbR`>`p^{c5#&6mEO zaOL)fhw>erosE8zHk7_LV^dO6Qj$+K<-Ibp7My_iNi1ELc!h>N>Nq;(|cSneR*t*Vaa}GX$`+ZA&`c(a~{2QY_s} zTZC%`cvPFw{?7+yDJiL#RWZviFI=;R2ee?%!NFnoOHn2U&q>Vvh8(6ZMQ7RkEU!p= zx7ovYF2CZ>6H3k#SDw%`+nc>tH7ezn?&I8Vw^y9K(LX8a=fy1Z*;n zpx{JF@MyZz(x8X8wq{$DoY)#|IA!Y8h5PpHi;eSDYq=s(H;ZNAvh%8mf4i)YS7
}S91b>DZ%_=)~0qYYPfdi<>Ne7l(* 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

=w;rLBAF7JNT<2t)%k$%aQHCw;msr|0_ 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 \inlineimage icons/plus.png - . + (Select \uicontrol View > \uicontrol Views > \uicontrol Assets to enable it, + if you can't find it). \li Select the asset files, and then select \uicontrol Open. \li Select the location where the files will be saved in the \uicontrol {Add Resources} dialog. @@ -180,8 +138,8 @@ To modify the \e Screen01 component in the \uicontrol {2D} view: \list 1 - \li Drag-and-drop the background image from \uicontrol Assets to the - rectangle in \l Navigator. + \li Drag-and-drop the background image (1) from \uicontrol Assets to the + \l{basic-rectangle}{Rectangle} (2) in \l Navigator. \image loginui1-library-assets.jpg "Assets view" \li \QDS automatically creates an instance of the \l{Images}{Image} component for you with the path to the image file set as the @@ -189,7 +147,7 @@ \image loginui1-image-properties.png "Image properties" \li Drag-and-drop the Qt logo from \uicontrol Assets to the rectangle in \uicontrol Navigator and move it to the top-center of the - background image in \uicontrol the {2D} view. + background image in the \uicontrol {2D} view. \li Select \e Text in \uicontrol Navigator and drag it below the logo in the \uicontrol {2D} view. If the text is hidden behind the background, select \inlineimage icons/navigator-arrowdown.png @@ -204,8 +162,8 @@ line: \e {Are you ready to explore?}. \image loginui1-text-properties.png "Text properties" \li In \uicontrol Font, select \e {Titillium Web ExtraLight}. - \li In \uicontrol Size, set the font size of the tag line to - \e 50 pixels (\uicontrol px). + \li In \uicontrol Size, first select the scale to pixels (\uicontrol px), + then set font size of the tag line to \e 50 (\uicontrol px). \li In \uicontrol {Text color}, set the text color to white (\e #ffffff). \endlist @@ -219,74 +177,6 @@ \image loginui1-main-page.jpg "Modified UI in the Design mode" You can resize the preview dialog to display the whole screen. - - \section2 Learn More - Components - - \QDS provides preset \l{glossary-component}{components} for creating - UIs, including components for creating and animating visual components, - receiving user input, and creating data models and views. - - To be able to use the functionality of preset components, the wizard template - adds the following \e import statements to the UI files (.ui.qml) that it - creates: - - \quotefromfile Loginui1/content/Screen01.ui.qml - \skipto import - \printuntil Controls - - You can view the import statements in the \uicontrol {Code} view. - - The \l Components view lists the components in each module that are - supported by \QDS. You can use the basic components to create your own - components, and they will be listed in \uicontrol {My Components}. - This section is only visible if you have created custom components. - - The \l {basic-rectangle}{Rectangle}, \l Text, and \l {Images}{Image} - components used in this tutorial are based on the \l Item component. - It is the base component for all visual elements, with implementation - of basic functions and properties, such as component type, ID, position, - size, and visibility. - - For more information, see \l{Use Case - Visual Elements In QML}. For - descriptions of all components, see \l{All QML Types} in the Qt reference - documentation. - - \section3 Regtangle Properties - - The default \l {basic-rectangle}{Rectangle} component is used for drawing - shapes with four sides and four corners. You can fill rectangles either with - a solid fill color or a gradient. You can specify the border color separately. - By setting the value of the radius property, you can create shapes with - rounded corners. - - If you want to specify the radius of each corner separately, you can use the - \l{studio-rectangle}{Rectangle} component from the - \uicontrol {Qt Quick Studio Components} module instead of the basic rectangle - component. It is available in \uicontrol Components - > \uicontrol {Qt Quick Studio Components}. - - \section3 Text Properties - - The \l Text component is used for adding static text to the UI, such as - titles and labels. You can select the font to use and specify extensive - properties for each text component, such as size in points or pixels, - weight, style, and spacing. - - If you want to create a label with a background, use the \l Label component - from the \uicontrol {Qt Quick Controls} module instead of the Text component. - - \section3 Image Properties - - The \l {Images}{Image} component is used for adding images to the UI in several - supported formats, including bitmap formats such as PNG and JPEG and vector - graphics formats such as SVG. To add an image to \uicontrol Assets, select - \inlineimage icons/plus.png - , and then select the image file. - - If you need to display animated images, use the \l {Animated Image} - component, also available in \uicontrol Components > - \uicontrol {Default Components} > \uicontrol Basic. - \section1 Creating a Push Button You can use another wizard template to create a push button and to add it to @@ -320,21 +210,9 @@ \image loginui1-button.png "Button in the Design mode." - \section2 Learn More - UI Controls - - The \e {Custom Button} wizard template creates a button component - based on the \l {Button} control in the \l {Qt Quick Controls} module. It - is a push-button control that can be pushed or clicked by the user. Buttons - are normally used to perform an action or to answer a question. The - properties and functionality inherited from the Button component enable - you to set text, display an icon, react to mouse clicks, and so on. - - To be able to use the functionality of the Button control, the wizard template - adds the following \e import statements to the \e EntryField.ui.qml file: - - \quotefromfile Loginui1/content/EntryField.ui.qml - \skipto import - \printuntil Controls + \note To open the \uicontrol States view, select it from + \uicontrol View > \uicontrol Views > \uicontrol States, if + it is not available by default. Next, you will change the appearance of the EntryField component by modifying its properties. @@ -375,8 +253,8 @@ properties in \uicontrol Properties. \li In \uicontrol Character > \uicontrol Font, select \e {Titillium Web ExtraLight}. - \li In \uicontrol Size, set the font size to \e 34 pixels - (\uicontrol px). + \li In \uicontrol Size, first select the scale to pixels (\uicontrol px), + then set font size to \e 34 (\uicontrol px). \li In \uicontrol {Text color}, set the text color to white (\e #ffffff). \li In \uicontrol {Alignment H}, select the \uicontrol Left button to @@ -396,6 +274,10 @@ \image loginui1-entry-field-styled.jpg "Modified button in the 2D view" + \note Do not edit the the value of \uicontrol Text in the \uicontrol Character + property, because this will break the connection, and later you won't be able + to change the text in \uicontrol {Button Content} > \uicontrol Text. + Next, you will add instances of the \e EntryField component to the \e Screen01 component and modify their properties. @@ -459,7 +341,8 @@ its properties in \uicontrol Properties. \li In \uicontrol Character > \uicontrol Font, select \e {Titillium Web ExtraLight}. - \li In \uicontrol Size, set the font size to \e 34 pixels. + \li In \uicontrol Size, first select the scale to pixels (\uicontrol px), + then set font size to \e 34 (\uicontrol px). \li In \uicontrol {Text color}, set the text color to \e #41cd52. \li In the \uicontrol States view, select the \e normal state and repeat the changes, as necessary. @@ -503,7 +386,136 @@ \image loginui1-ready.jpg "The finished UI in the 2D view" - \section2 Learn More - Component IDs + \section1 Learn More + The \e {Learn More} sections provide additional information about the + tasks performed by the wizards and about other basic tasks and concepts. + + \section2 Projects and Files + \QDS creates a set of files and folders that you need to create + a UI. The files are listed in the \l{File System} view. + + \image loginui1-project-files.png + \list + \li The \e {loginui1.qmlproject} project file defines that all + component, JavaScript, and image files in the project folder belong + to the project. Therefore, you do not need to individually list new + files when you add them to the project. + \li The \e {loginui1.qml} file defines the functionality of + the UI. For the time being, it does not do anything. + \li The \e {Screen01.ui.qml} file is a custom component created by + the wizard template. For more information, see \l {UI Files}. + + By default, this is the main file in the project, but you can + change that in the .qmlproject file. While the custom component + is a good starting point for new users, you don't have to use it. + Specifically, if you export and import designs using \QB, your main + file is most likely called something else. For more information, + see \l {Exporting from Design Tools}. + \li The \e CMakeLists.txt project configuration file allowing you to + share your project as a fully working C++ application with + developers. + \li The \e {qtquickcontrols2.conf} file specifies the selected + \l {Styling Qt Quick Controls}{UI style} and some style-specific + arguments. + \li The \e imports folder contains \e {Constants.qml} and + \e {DirectoryFontLoader.qml} files that specify a font loader + and a \e qmldir module definition file that declares the Constant + component. For more information, see + \l {Module Definition qmldir Files}. The \e EventListModel.qml and + \e EventListSimulator.qml files are not used in this example, so + you can ignore them for now. + \endlist + \l{UI Files}{UI files} define a hierarchy of components with a + highly-readable, structured layout. Every UI file consists of two parts: + an imports section and an component declaration section. The components and + functionality most common to UIs are provided in the \c QtQuick import. You + can view the code of a \e .ui.qml file in the \l{Code} view. + + + \section2 Components + + \QDS provides preset \l{glossary-component}{components} for creating + UIs, including components for creating and animating visual components, + receiving user input, and creating data models and views. + + To be able to use the functionality of preset components, the wizard template + adds the following \e import statements to the UI files (.ui.qml) that it + creates: + + \quotefromfile Loginui1/content/Screen01.ui.qml + \skipto import + \printuntil Controls + + You can view the import statements in the \uicontrol {Code} view. + + The \l Components view lists the components in each module that are + supported by \QDS. You can use the basic components to create your own + components, and they will be listed in \uicontrol {My Components}. + This section is only visible if you have created custom components. + + The \l {basic-rectangle}{Rectangle}, \l Text, and \l {Images}{Image} + components used in this tutorial are based on the \l Item component. + It is the base component for all visual elements, with implementation + of basic functions and properties, such as component type, ID, position, + size, and visibility. + + For more information, see \l{Use Case - Visual Elements In QML}. For + descriptions of all components, see \l{All QML Types} in the Qt reference + documentation. + + \section3 Regtangle Properties + + The default \l {basic-rectangle}{Rectangle} component is used for drawing + shapes with four sides and four corners. You can fill rectangles either with + a solid fill color or a gradient. You can specify the border color separately. + By setting the value of the radius property, you can create shapes with + rounded corners. + + If you want to specify the radius of each corner separately, you can use the + \l{studio-rectangle}{Rectangle} component from the + \uicontrol {Qt Quick Studio Components} module instead of the basic rectangle + component. It is available in \uicontrol Components + > \uicontrol {Qt Quick Studio Components}. + + \section3 Text Properties + + The \l Text component is used for adding static text to the UI, such as + titles and labels. You can select the font to use and specify extensive + properties for each text component, such as size in points or pixels, + weight, style, and spacing. + + If you want to create a label with a background, use the \l Label component + from the \uicontrol {Qt Quick Controls} module instead of the Text component. + + \section3 Image Properties + + The \l {Images}{Image} component is used for adding images to the UI in several + supported formats, including bitmap formats such as PNG and JPEG and vector + graphics formats such as SVG. To add an image to \uicontrol Assets, select + \inlineimage icons/plus.png + , and then select the image file. + + If you need to display animated images, use the \l {Animated Image} + component, also available in \uicontrol Components > + \uicontrol {Default Components} > \uicontrol Basic. + + \section2 UI Controls + + The \e {Custom Button} wizard template creates a button component + based on the \l {Button} control in the \l {Qt Quick Controls} module. It + is a push-button control that can be pushed or clicked by the user. Buttons + are normally used to perform an action or to answer a question. The + properties and functionality inherited from the Button component enable + you to set text, display an icon, react to mouse clicks, and so on. + + To be able to use the functionality of the Button control, the wizard template + adds the following \e import statements to the \e EntryField.ui.qml file: + + \quotefromfile Loginui1/content/EntryField.ui.qml + \skipto import + \printuntil Controls + + \section2 Component IDs Each component and each instance of a component has an \e ID that uniquely identifies it and enables other components' properties to be bound to it. From a0af1fa9278919f978b9cd26dfad69193f2dff30 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Wed, 12 Oct 2022 14:52:15 +0200 Subject: [PATCH 018/143] Utils: Keep User specified SideBar entries in file dialogs Change-Id: I0b218e889f351a2dc635aca147b82d752ba85205 Reviewed-by: hjk --- src/libs/utils/fileutils.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index 821a31caf09..42d8f553894 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -494,12 +494,22 @@ void prepareNonNativeDialog(QFileDialog &dialog) // Checking QFileDialog::itemDelegate() seems to be the only way to determine // whether the dialog is native or not. if (dialog.itemDelegate()) { - QList sideBarUrls; - for (const FilePath &path : FSEngine::registeredDeviceRoots()) { - if (path.exists()) - sideBarUrls.append(filePathToQUrl(path)); + FilePaths sideBarPaths; + + // Check existing urls, remove paths that need a device and no longer exist. + for (const QUrl &url : dialog.sidebarUrls()) { + FilePath path = qUrlToFilePath(url); + if (!path.needsDevice() || path.exists()) + sideBarPaths.append(path); } - dialog.setSidebarUrls(sideBarUrls); + + // Add all device roots that are not already in the sidebar and exist. + for (const FilePath &path : FSEngine::registeredDeviceRoots()) { + if (!sideBarPaths.contains(path) && path.exists()) + sideBarPaths.append(path); + } + + dialog.setSidebarUrls(Utils::transform(sideBarPaths, filePathToQUrl)); dialog.setIconProvider(Utils::FileIconProvider::iconProvider()); } } From 9b47b1575a84a35a6037d0f13649789c6298cc89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Mu=C4=87ko?= Date: Tue, 27 Sep 2022 23:10:46 +0200 Subject: [PATCH 019/143] McuSupport: 3rd party packages creation tests Change-Id: I0f9ed6c49b28007c617627525fc081ae9152e3f1 Reviewed-by: hjk --- src/plugins/mcusupport/mcupackage.cpp | 6 +- src/plugins/mcusupport/mcupackage.h | 4 +- src/plugins/mcusupport/mcusupportsdk.cpp | 18 +- ...mgcc_stm32f469i_discovery_baremetal_json.h | 1 + .../test/ghs_rh850_d1m1a_baremetal_json.h | 5 +- .../iar_stm32f469i_discovery_baremetal_json.h | 1 + src/plugins/mcusupport/test/unittest.cpp | 222 ++++++++++++++---- src/plugins/mcusupport/test/unittest.h | 4 + 8 files changed, 195 insertions(+), 66 deletions(-) diff --git a/src/plugins/mcusupport/mcupackage.cpp b/src/plugins/mcusupport/mcupackage.cpp index 081eade8abc..fbe9a015f34 100644 --- a/src/plugins/mcusupport/mcupackage.cpp +++ b/src/plugins/mcusupport/mcupackage.cpp @@ -37,15 +37,13 @@ McuPackage::McuPackage(const SettingsHandler::Ptr &settingsHandler, const QStringList &versions, const QString &downloadUrl, const McuPackageVersionDetector *versionDetector, - const bool addToSystemPath, - const FilePath &relativePathModifier) + const bool addToSystemPath) : settingsHandler(settingsHandler) , m_label(label) , m_defaultPath(settingsHandler->getPath(settingsKey, QSettings::SystemScope, defaultPath)) , m_detectionPath(detectionPath) , m_settingsKey(settingsKey) , m_versionDetector(versionDetector) - , m_relativePathModifier(relativePathModifier) , m_versions(versions) , m_cmakeVariableName(cmakeVarName) , m_environmentVariableName(envVarName) @@ -100,7 +98,7 @@ FilePath McuPackage::basePath() const FilePath McuPackage::path() const { - return (basePath() / m_relativePathModifier.path()).cleanPath(); + return basePath().cleanPath(); } FilePath McuPackage::defaultPath() const diff --git a/src/plugins/mcusupport/mcupackage.h b/src/plugins/mcusupport/mcupackage.h index 8d045f8dc02..631b514998b 100644 --- a/src/plugins/mcusupport/mcupackage.h +++ b/src/plugins/mcusupport/mcupackage.h @@ -40,8 +40,7 @@ public: const QStringList &versions = {}, const QString &downloadUrl = {}, const McuPackageVersionDetector *versionDetector = nullptr, - const bool addToPath = false, - const Utils::FilePath &relativePathModifier = Utils::FilePath()); + const bool addToPath = false); ~McuPackage() override = default; @@ -85,7 +84,6 @@ private: QScopedPointer m_versionDetector; Utils::FilePath m_path; - Utils::FilePath m_relativePathModifier; // relative path to m_path to be returned by path() QString m_detectedVersion; QStringList m_versions; const QString m_cmakeVariableName; diff --git a/src/plugins/mcusupport/mcusupportsdk.cpp b/src/plugins/mcusupport/mcusupportsdk.cpp index f9213d00f7c..b3f3a570cfa 100644 --- a/src/plugins/mcusupport/mcusupportsdk.cpp +++ b/src/plugins/mcusupport/mcusupportsdk.cpp @@ -361,15 +361,12 @@ McuPackagePtr createStm32CubeProgrammerPackage(const SettingsHandler::Ptr &setti { FilePath defaultPath; const QString cubePath = "STMicroelectronics/STM32Cube/STM32CubeProgrammer"; - if (HostOsInfo::isWindowsHost()) { - const FilePath programPath = findInProgramFiles(cubePath); - if (!programPath.isEmpty()) - defaultPath = programPath; - } else { - const FilePath programPath = FileUtils::homePath() / cubePath; - if (programPath.exists()) - defaultPath = programPath; - } + if (HostOsInfo::isWindowsHost()) + defaultPath = findInProgramFiles(cubePath) / "bin"; + else + defaultPath = FileUtils::homePath() / cubePath / "bin"; + if (!defaultPath.exists()) + FilePath defaultPath = {}; const FilePath detectionPath = FilePath::fromUserInput( QLatin1String(Utils::HostOsInfo::isWindowsHost() ? "/bin/STM32_Programmer_CLI.exe" @@ -386,8 +383,7 @@ McuPackagePtr createStm32CubeProgrammerPackage(const SettingsHandler::Ptr &setti {}, // versions "https://www.st.com/en/development-tools/stm32cubeprog.html", // download url nullptr, // version detector - true, // add to path - "/bin" // relative path modifier + true // add to path )}; } diff --git a/src/plugins/mcusupport/test/armgcc_stm32f469i_discovery_baremetal_json.h b/src/plugins/mcusupport/test/armgcc_stm32f469i_discovery_baremetal_json.h index 763e039b548..27f8a85d96d 100644 --- a/src/plugins/mcusupport/test/armgcc_stm32f469i_discovery_baremetal_json.h +++ b/src/plugins/mcusupport/test/armgcc_stm32f469i_discovery_baremetal_json.h @@ -18,6 +18,7 @@ constexpr auto armgcc_stm32f469i_discovery_baremetal_json = R"( "id": "STM32CubeProgrammer_PATH", "label": "STM32CubeProgrammer", "type": "path", + "setting": "Stm32CubeProgrammer", "defaultValue": { "windows": "%{Env:PROGRAMSANDFILES}/STMicroelectronics/STM32Cube/STM32CubeProgrammer/", "unix": "%{Env:HOME}/STMicroelectronics/STM32Cube/STM32CubeProgrammer/" diff --git a/src/plugins/mcusupport/test/ghs_rh850_d1m1a_baremetal_json.h b/src/plugins/mcusupport/test/ghs_rh850_d1m1a_baremetal_json.h index bac50527196..40127e894f0 100644 --- a/src/plugins/mcusupport/test/ghs_rh850_d1m1a_baremetal_json.h +++ b/src/plugins/mcusupport/test/ghs_rh850_d1m1a_baremetal_json.h @@ -17,14 +17,15 @@ constexpr auto ghs_rh850_d1m1a_baremetal_json = R"( { "id": "FlashProgrammer_path", "setting": "FlashProgrammerPath", - "label": "Path to Renesas Flash Programmer", + "label": "Renesas Flash Programmer", "type": "path", + "setting": "RenesasFlashProgrammer", "cmakeVar": "RENESAS_FLASH_PROGRAMMER_PATH", "defaultValue": { "windows": "%{Env:PROGRAMSANDFILES}/Renesas Electronics/Programming Tools/Renesas Flash Programmer V3.09", "unix": "%{Env:HOME}" }, - "envVar": "RenesasFlashProgrammer_PATH", + "envVar": "RENESAS_FLASH_PROGRAMMER_PATH", "optional": true, "addToSystemPath": true } diff --git a/src/plugins/mcusupport/test/iar_stm32f469i_discovery_baremetal_json.h b/src/plugins/mcusupport/test/iar_stm32f469i_discovery_baremetal_json.h index 74f1012f61c..6b1edf70b0d 100644 --- a/src/plugins/mcusupport/test/iar_stm32f469i_discovery_baremetal_json.h +++ b/src/plugins/mcusupport/test/iar_stm32f469i_discovery_baremetal_json.h @@ -18,6 +18,7 @@ constexpr auto iar_stm32f469i_discovery_baremetal_json = R"( "id": "STM32CubeProgrammer_PATH", "label": "STM32CubeProgrammer", "type": "path", + "setting": "Stm32CubeProgrammer", "defaultValue": { "windows": "%{Env:PROGRAMSANDFILES}/STMicroelectronics/STM32Cube/STM32CubeProgrammer/", "unix": "%{Env:HOME}/STMicroelectronics/STM32Cube/STM32CubeProgrammer/" diff --git a/src/plugins/mcusupport/test/unittest.cpp b/src/plugins/mcusupport/test/unittest.cpp index 64301f3009d..016b9eb7805 100644 --- a/src/plugins/mcusupport/test/unittest.cpp +++ b/src/plugins/mcusupport/test/unittest.cpp @@ -3,6 +3,7 @@ #include "unittest.h" +#include "armgcc_ek_ra6m3g_baremetal_json.h" #include "armgcc_mimxrt1050_evk_freertos_json.h" #include "armgcc_mimxrt1064_evk_freertos_json.h" #include "armgcc_mimxrt1170_evk_freertos_json.h" @@ -11,6 +12,7 @@ #include "errors_json.h" #include "gcc_desktop_json.h" #include "ghs_rh850_d1m1a_baremetal_json.h" +#include "ghs_tviic2d6m_baremetal_json.h" #include "iar_mimxrt1064_evk_freertos_json.h" #include "iar_stm32f469i_discovery_baremetal_json.h" #include "msvc_desktop_json.h" @@ -123,6 +125,43 @@ const char vendor[]{"target_vendor"}; const QString settingsPrefix = QLatin1String(Constants::SETTINGS_GROUP) + '/' + QLatin1String(Constants::SETTINGS_KEY_PACKAGE_PREFIX); +const char defaultToolPath[]{"/opt/biz/foo"}; +const char xpressoIdePath[]{"/usr/local/mcuxpressoide"}; +const char xpressoIdeLabel[]{"MCUXpresso IDE"}; +const char xpressoIdeSetting[]{"MCUXpressoIDE"}; +const char xpressoIdeCmakeVar[]{"MCUXPRESSO_IDE_PATH"}; +const char xpressoIdeEnvVar[]{"MCUXpressoIDE_PATH"}; +const char xpressoIdeDetectionPath[]{"ide/binaries/crt_emu_cm_redlink"}; + +const char stmCubeProgrammerSetting[]{"Stm32CubeProgrammer"}; +const char stmCubeProgrammerLabel[]{"STM32CubeProgrammer"}; +const QString stmCubeProgrammerPath{QString{defaultToolPath} + "/bin"}; +const QString stmCubeProgrammerDetectionPath{"/bin/STM32_Programmer.sh"}; + +const char renesasProgrammerSetting[]{"RenesasFlashProgrammer"}; +const char renesasProgrammerCmakeVar[]{"RENESAS_FLASH_PROGRAMMER_PATH"}; +const QString renesasProgrammerEnvVar{renesasProgrammerCmakeVar}; +const char renesasProgrammerLabel[]{"Renesas Flash Programmer"}; +const char renesasProgrammerDetectionPath[]{"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"}; +const char cypressProgrammerEnvVar[]{"CYPRESS_AUTO_FLASH_UTILITY_DIR"}; +const char cypressProgrammerLabel[]{"Cypress Auto Flash Utility"}; +const char cypressProgrammerDetectionPath[]{"/bin/openocd"}; + +const char jlinkPath[]{"/opt/SEGGER/JLink"}; +const char jlinkSetting[]{"JLinkPath"}; +const char jlinkCmakeVar[]{"JLINK_PATH"}; +const char jlinkEnvVar[]{"JLINK_PATH"}; +const char jlinkLabel[]{"Path to SEGGER J-Link"}; + const QString unsupportedToolchainFilePath = QString{qtForMcuSdkPath} + "/lib/cmake/Qul/toolchain/unsupported.cmake"; @@ -282,6 +321,7 @@ void verifyPackage(const McuPackagePtr &package, const QString &cmakeVar, const QString &envVar, const QString &label, + const QString &detectionPath, const QStringList &versions) { QVERIFY(package); @@ -290,6 +330,7 @@ void verifyPackage(const McuPackagePtr &package, QCOMPARE(package->cmakeVariableName(), cmakeVar); QCOMPARE(package->environmentVariableName(), envVar); QCOMPARE(package->label(), label); + QCOMPARE(package->detectionPath().toString(), detectionPath); QCOMPARE(package->settingsKey(), setting); QCOMPARE(package->versions(), versions); } @@ -928,11 +969,12 @@ void McuSupportTest::test_createTargetWithToolchainPackages() verifyPackage(qtForMCUsSDK, qtForMcuSdkPath, - {}, // qtForMcuSdkPath + {}, Constants::SETTINGS_KEY_PACKAGE_QT_FOR_MCUS_SDK, QUL_CMAKE_VAR, QUL_ENV_VAR, QUL_LABEL, + {}, {}); verifyTargetToolchains(targets, @@ -1372,76 +1414,164 @@ void McuSupportTest::test_resolveCmakeVariablesInDefaultPath() void McuSupportTest::test_legacy_createThirdPartyPackage_data() { - const QString defaultToolPath{"/opt/biz/foo"}; - - const char xpressoIdeSetting[]{"MCUXpressoIDE"}; - const char xpressoIdeCmakeVar[]{"MCUXPRESSO_IDE_PATH"}; - const char xpressoIdeEnvVar[]{"MCUXpressoIDE_PATH"}; - - const char stmCubeProgrammerSetting[]{"Stm32CubeProgrammer"}; - const QString stmCubeProgrammerPath{defaultToolPath + "/bin"}; - - const char renesasProgrammerSetting[]{"RenesasFlashProgrammer"}; - const char renesasProgrammerCmakeVar[]{"RENESAS_FLASH_PROGRAMMER_PATH"}; - const QString renesasProgrammerEnvVar{renesasProgrammerCmakeVar}; - - const char cypressProgrammerSetting[]{"CypressAutoFlashUtil"}; - const char cypressProgrammerCmakeVar[]{"INFINEON_AUTO_FLASH_UTILITY_DIR"}; - const char cypressProgrammerEnvVar[]{"CYPRESS_AUTO_FLASH_UTILITY_DIR"}; - QTest::addColumn("creator"); + QTest::addColumn("json"); QTest::addColumn("path"); QTest::addColumn("defaultPath"); QTest::addColumn("setting"); QTest::addColumn("cmakeVar"); QTest::addColumn("envVar"); + QTest::addColumn("label"); + QTest::addColumn("detectionPath"); - QTest::newRow("mcuXpresso") << PackageCreator{[this]() { - return Legacy::createMcuXpressoIdePackage(settingsMockPtr); - }} << defaultToolPath << defaultToolPath - << xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar; - QTest::newRow("stmCubeProgrammer") << PackageCreator{[this]() { - return Legacy::createStm32CubeProgrammerPackage(settingsMockPtr); - }} << stmCubeProgrammerPath << defaultToolPath - << stmCubeProgrammerSetting << empty << empty; + QTest::newRow("armgcc_mimxrt1050_evk_freertos_json mcuXpresso") + << PackageCreator{[this]() { return Legacy::createMcuXpressoIdePackage(settingsMockPtr); }} + << armgcc_mimxrt1050_evk_freertos_json << xpressoIdePath << xpressoIdePath + << xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel + << xpressoIdeDetectionPath; - QTest::newRow("renesasProgrammer") << PackageCreator{[this]() { - return Legacy::createRenesasProgrammerPackage(settingsMockPtr); - }} << defaultToolPath << defaultToolPath - << renesasProgrammerSetting << renesasProgrammerCmakeVar - << renesasProgrammerEnvVar; + QTest::newRow("armgcc_mimxrt1064_evk_freertos_json mcuXpresso") + << PackageCreator{[this]() { return Legacy::createMcuXpressoIdePackage(settingsMockPtr); }} + << armgcc_mimxrt1064_evk_freertos_json << xpressoIdePath << xpressoIdePath + << xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel + << xpressoIdeDetectionPath; - QTest::newRow("cypressProgrammer") << PackageCreator{[this]() { - return Legacy::createCypressProgrammerPackage(settingsMockPtr); - }} << defaultToolPath << defaultToolPath - << cypressProgrammerSetting << cypressProgrammerCmakeVar - << cypressProgrammerEnvVar; + QTest::newRow("armgcc_mimxrt1170_evk_freertos_json mcuXpresso") + << PackageCreator{[this]() { return Legacy::createMcuXpressoIdePackage(settingsMockPtr); }} + << armgcc_mimxrt1170_evk_freertos_json << xpressoIdePath << xpressoIdePath + << xpressoIdeSetting << xpressoIdeCmakeVar << xpressoIdeEnvVar << xpressoIdeLabel + << xpressoIdeDetectionPath; + + QTest::newRow("armgcc_stm32h750b_discovery_baremetal_json stmCubeProgrammer") + << PackageCreator{[this]() { + return Legacy::createStm32CubeProgrammerPackage(settingsMockPtr); + }} + << armgcc_stm32h750b_discovery_baremetal_json << stmCubeProgrammerPath + << stmCubeProgrammerPath << stmCubeProgrammerSetting << empty << empty + << stmCubeProgrammerLabel << stmCubeProgrammerDetectionPath; + QTest::newRow("armgcc_stm32f769i_discovery_freertos_json stmCubeProgrammer") + << PackageCreator{[this]() { + return Legacy::createStm32CubeProgrammerPackage(settingsMockPtr); + }} + << armgcc_stm32f769i_discovery_freertos_json << stmCubeProgrammerPath + << stmCubeProgrammerPath << stmCubeProgrammerSetting << empty << empty + << stmCubeProgrammerLabel << stmCubeProgrammerDetectionPath; + QTest::newRow("ghs_rh850_d1m1a_baremetal_json renesasProgrammer") + << PackageCreator{[this]() { + return Legacy::createRenesasProgrammerPackage(settingsMockPtr); + }} + << ghs_rh850_d1m1a_baremetal_json << defaultToolPath << defaultToolPath + << renesasProgrammerSetting << renesasProgrammerCmakeVar << renesasProgrammerEnvVar + << renesasProgrammerLabel << renesasProgrammerDetectionPath; } void McuSupportTest::test_legacy_createThirdPartyPackage() { QFETCH(PackageCreator, creator); + QFETCH(QString, json); QFETCH(QString, path); QFETCH(QString, defaultPath); QFETCH(QString, setting); QFETCH(QString, cmakeVar); QFETCH(QString, envVar); - - if (!envVar.isEmpty()) - QVERIFY(qputenv(envVar.toLocal8Bit(), defaultPath.toLocal8Bit())); + QFETCH(QString, label); + QFETCH(QString, detectionPath); EXPECT_CALL(*settingsMockPtr, getPath(QString{setting}, _, _)) .Times(2) .WillRepeatedly(Return(FilePath::fromUserInput(defaultPath))); - McuPackagePtr thirdPartyPacakge{creator()}; - QVERIFY(thirdPartyPacakge); - QCOMPARE(thirdPartyPacakge->settingsKey(), setting); - QCOMPARE(thirdPartyPacakge->environmentVariableName(), envVar); - QCOMPARE(thirdPartyPacakge->path().toString(), path); + McuPackagePtr thirdPartyPackage{creator()}; + verifyPackage(thirdPartyPackage, + path, + defaultPath, + setting, + cmakeVar, + envVar, + label, + detectionPath, + {}); +} - if (!envVar.isEmpty()) - QVERIFY(qunsetenv(envVar.toLocal8Bit())); +void McuSupportTest::test_createThirdPartyPackage_data() +{ + test_legacy_createThirdPartyPackage_data(); +} + +void McuSupportTest::test_createThirdPartyPackage() +{ + QFETCH(QString, json); + QFETCH(QString, path); + QFETCH(QString, defaultPath); + QFETCH(QString, setting); + QFETCH(QString, cmakeVar); + QFETCH(QString, envVar); + QFETCH(QString, label); + + McuTargetDescription targetDescription{parseDescriptionJson(json.toLocal8Bit())}; + + EXPECT_CALL(*settingsMockPtr, getPath(QString{setting}, QSettings::SystemScope, _)) + .Times(testing::AtMost(1)) + .WillOnce(Return(FilePath::fromUserInput(defaultPath))); + + EXPECT_CALL(*settingsMockPtr, getPath(QString{setting}, QSettings::UserScope, _)) + .Times(testing::AtMost(1)) + .WillOnce(Return(FilePath::fromUserInput(path))); + + auto [targets, packages] = targetFactory.createTargets(targetDescription, sdkPackagePtr); + + auto thirdPartyPackage = findOrDefault(packages, [&setting](const McuPackagePtr &pkg) { + return (pkg->settingsKey() == setting); + }); + + verifyPackage(thirdPartyPackage, path, defaultPath, setting, cmakeVar, envVar, label, {}, {}); +} + +void McuSupportTest::test_legacy_createCypressProgrammer3rdPartyPackage() +{ + EXPECT_CALL(*settingsMockPtr, getPath(QString{cypressProgrammerSetting}, _, _)) + .Times(2) + .WillRepeatedly(Return(FilePath::fromUserInput(defaultToolPath))); + + McuPackagePtr thirdPartyPackage{Legacy::createCypressProgrammerPackage(settingsMockPtr)}; + verifyPackage(thirdPartyPackage, + defaultToolPath, + defaultToolPath, + cypressProgrammerSetting, + cypressProgrammerCmakeVar, + cypressProgrammerEnvVar, + cypressProgrammerLabel, + cypressProgrammerDetectionPath, + {}); +} + +void McuSupportTest::test_createJLink3rdPartyPackage() +{ + McuTargetDescription targetDescription{parseDescriptionJson(armgcc_ek_ra6m3g_baremetal_json)}; + + EXPECT_CALL(*settingsMockPtr, getPath(QString{jlinkSetting}, QSettings::SystemScope, _)) + .Times(testing::AtMost(1)) + .WillOnce(Return(FilePath::fromUserInput(jlinkPath))); + + EXPECT_CALL(*settingsMockPtr, getPath(QString{jlinkSetting}, QSettings::UserScope, _)) + .Times(testing::AtMost(1)) + .WillOnce(Return(FilePath::fromUserInput(jlinkPath))); + + auto [targets, packages] = targetFactory.createTargets(targetDescription, sdkPackagePtr); + + auto thirdPartyPackage = findOrDefault(packages, [](const McuPackagePtr &pkg) { + return (pkg->settingsKey() == jlinkSetting); + }); + + verifyPackage(thirdPartyPackage, + jlinkPath, + jlinkPath, + jlinkSetting, + jlinkCmakeVar, + jlinkEnvVar, + jlinkLabel, + {}, + {}); } void McuSupportTest::test_defaultValueForEachOperationSystem() diff --git a/src/plugins/mcusupport/test/unittest.h b/src/plugins/mcusupport/test/unittest.h index be46281beba..1752f52f1be 100644 --- a/src/plugins/mcusupport/test/unittest.h +++ b/src/plugins/mcusupport/test/unittest.h @@ -94,6 +94,10 @@ private slots: void test_legacy_createThirdPartyPackage_data(); void test_legacy_createThirdPartyPackage(); + void test_createThirdPartyPackage_data(); + void test_createThirdPartyPackage(); + void test_legacy_createCypressProgrammer3rdPartyPackage(); + void test_createJLink3rdPartyPackage(); void test_defaultValueForEachOperationSystem(); void test_addToSystemPathFlag(); From 9908f623f66f57b472f172a1a00e1afe40271744 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 12 Oct 2022 17:17:44 +0200 Subject: [PATCH 020/143] FilePath: Fix sdktool build Change-Id: I0f8b2c8b3fb36580ee95d439d8034397e42eec1f Reviewed-by: hjk --- src/libs/utils/filepath.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h index 055e0f135fb..01100dc1320 100644 --- a/src/libs/utils/filepath.h +++ b/src/libs/utils/filepath.h @@ -15,6 +15,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE class QDateTime; From da0cb254b2f48c4ec44b975df30513c6f826ea6c Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Wed, 12 Oct 2022 17:23:37 +0300 Subject: [PATCH 021/143] CMake: Support Build File also from header files Fixes: QTCREATORBUG-26164 Change-Id: Iaa2fdd34cffad07be668ca7142a8ffa2c373d325 Reviewed-by: Cristian Adam --- .../cmakeprojectmanager/cmakeprojectmanager.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp index 33d9684acbc..eaf2261b06c 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -217,10 +218,17 @@ void CMakeManager::buildFile(Node *node) CMakeTargetNode *targetNode = dynamic_cast(fileNode->parentProjectNode()); if (!targetNode) return; + Utils::FilePath filePath = fileNode->filePath(); + if (filePath.fileName().contains(".h")) { + bool wasHeader = false; + const QString sourceFile = CppEditor::correspondingHeaderOrSource(filePath.toString(), &wasHeader); + if (wasHeader && !sourceFile.isEmpty()) + filePath = Utils::FilePath::fromString(sourceFile); + } Target *target = project->activeTarget(); QTC_ASSERT(target, return); const QString generator = CMakeGeneratorKitAspect::generator(target->kit()); - const QString relativeSource = fileNode->filePath().relativeChildPath(targetNode->filePath()).toString(); + const QString relativeSource = filePath.relativeChildPath(targetNode->filePath()).toString(); const QString objExtension = Utils::HostOsInfo::isWindowsHost() ? QString(".obj") : QString(".o"); Utils::FilePath targetBase; BuildConfiguration *bc = target->activeBuildConfiguration(); From 35de9566b6b83d9ac42903fb1fabb0b4dca3f5f0 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Wed, 12 Oct 2022 14:09:37 +0200 Subject: [PATCH 022/143] MainWindow: Use FileUtils::get... functions Changes the MainWindow to use FileUtils::getFilePaths function to access files on devices instead of QFileDialog. Change-Id: I0981c960b643edd69510cfed1cce16346962d75a Reviewed-by: Eike Ziller --- src/plugins/coreplugin/documentmanager.cpp | 5 +-- src/plugins/coreplugin/documentmanager.h | 4 ++- .../editormanager/editormanager.cpp | 4 +-- .../coreplugin/editormanager/editormanager.h | 3 +- src/plugins/coreplugin/mainwindow.cpp | 32 +------------------ 5 files changed, 11 insertions(+), 37 deletions(-) diff --git a/src/plugins/coreplugin/documentmanager.cpp b/src/plugins/coreplugin/documentmanager.cpp index c19e65366b5..48c2227a3b1 100644 --- a/src/plugins/coreplugin/documentmanager.cpp +++ b/src/plugins/coreplugin/documentmanager.cpp @@ -1032,11 +1032,12 @@ void DocumentManager::showFilePropertiesDialog(const FilePath &filePath) FilePaths DocumentManager::getOpenFileNames(const QString &filters, const FilePath &pathIn, - QString *selectedFilter) + QString *selectedFilter, + QFileDialog::Options options) { const FilePath path = pathIn.isEmpty() ? fileDialogInitialDirectory() : pathIn; const FilePaths files = FileUtils::getOpenFilePaths(nullptr, tr("Open File"), path, filters, - selectedFilter); + selectedFilter, options); if (!files.isEmpty()) setFileDialogLastVisitedDirectory(files.front().absolutePath()); return files; diff --git a/src/plugins/coreplugin/documentmanager.h b/src/plugins/coreplugin/documentmanager.h index c2ecf69d8d8..07d3e0b890d 100644 --- a/src/plugins/coreplugin/documentmanager.h +++ b/src/plugins/coreplugin/documentmanager.h @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -64,7 +65,8 @@ public: static Utils::FilePaths getOpenFileNames(const QString &filters, const Utils::FilePath &path = {}, - QString *selectedFilter = nullptr); + QString *selectedFilter = nullptr, + QFileDialog::Options options = {}); static Utils::FilePath getSaveFileName(const QString &title, const Utils::FilePath &pathIn, const QString &filter = {}, diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index 54622fcaf73..c0044573f50 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -3249,11 +3249,11 @@ void EditorManager::addCloseEditorListener(const std::function \sa DocumentManager::getOpenFileNames() */ -FilePaths EditorManager::getOpenFilePaths() +FilePaths EditorManager::getOpenFilePaths(QFileDialog::Options options) { QString selectedFilter; const QString &fileFilters = DocumentManager::fileDialogFilter(&selectedFilter); - return DocumentManager::getOpenFileNames(fileFilters, {}, &selectedFilter); + return DocumentManager::getOpenFileNames(fileFilters, {}, &selectedFilter, options); } static QString makeTitleUnique(QString *titlePattern) diff --git a/src/plugins/coreplugin/editormanager/editormanager.h b/src/plugins/coreplugin/editormanager/editormanager.h index ab285328b48..928f9c717bc 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.h +++ b/src/plugins/coreplugin/editormanager/editormanager.h @@ -12,6 +12,7 @@ #include "utils/link.h" #include "utils/textfileformat.h" +#include #include #include @@ -90,7 +91,7 @@ public: static bool openExternalEditor(const Utils::FilePath &filePath, Utils::Id editorId); static void addCloseEditorListener(const std::function &listener); - static Utils::FilePaths getOpenFilePaths(); + static Utils::FilePaths getOpenFilePaths(QFileDialog::Options options = {}); static IDocument *currentDocument(); static IEditor *currentEditor(); diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp index a7e943c1961..a872320cafc 100644 --- a/src/plugins/coreplugin/mainwindow.cpp +++ b/src/plugins/coreplugin/mainwindow.cpp @@ -1078,37 +1078,7 @@ void MainWindow::openFileWith() void MainWindow::openFileFromDevice() { - QSettings *settings = PluginManager::settings(); - settings->beginGroup(QLatin1String(settingsGroup)); - QVariant dialogSettings = settings->value(QLatin1String(openFromDeviceDialogKey)); - - QFileDialog dialog; - dialog.setOption(QFileDialog::DontUseNativeDialog); - if (!dialogSettings.isNull()) { - dialog.restoreState(dialogSettings.toByteArray()); - } - QList sideBarUrls = Utils::transform(Utils::filtered(FSEngine::registeredDeviceRoots(), - [](const auto &filePath) { - return filePath.exists(); - }), - [](const auto &filePath) { - return QUrl::fromLocalFile(filePath.toFSPathString()); - }); - dialog.setSidebarUrls(sideBarUrls); - dialog.setFileMode(QFileDialog::AnyFile); - - dialog.setIconProvider(FileIconProvider::iconProvider()); - - if (dialog.exec()) { - FilePaths filePaths = Utils::transform(dialog.selectedFiles(), [](const auto &path) { - return FilePath::fromString(path); - }); - - openFiles(filePaths, ICore::SwitchMode); - } - - settings->setValue(QLatin1String(openFromDeviceDialogKey), dialog.saveState()); - settings->endGroup(); + openFiles(EditorManager::getOpenFilePaths(QFileDialog::DontUseNativeDialog), ICore::SwitchMode); } IContext *MainWindow::contextObject(QWidget *widget) const From e5d6f5ce823cb5d967634dbab0bd803b77b05341 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Wed, 12 Oct 2022 14:00:32 +0200 Subject: [PATCH 023/143] ProjectExplorer: measure toolchain auto detection time Change-Id: I1c0be35b6119c126f1554475f0a96c34ef14c43c Reviewed-by: Cristian Adam --- .../projectexplorer/toolchainsettingsaccessor.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp index 8090867f5cc..41a6b584737 100644 --- a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp +++ b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp @@ -12,11 +12,16 @@ #include +#include +#include + using namespace Utils; namespace ProjectExplorer { namespace Internal { +static Q_LOGGING_CATEGORY(Log, "qtc.projectexplorer.toolchain.autodetection", QtWarningMsg) + // -------------------------------------------------------------------- // ToolChainSettingsUpgraders: // -------------------------------------------------------------------- @@ -49,8 +54,12 @@ struct ToolChainOperations static Toolchains autoDetectToolChains(const ToolchainDetector &detector) { Toolchains result; - for (ToolChainFactory *f : ToolChainFactory::allToolChainFactories()) + for (ToolChainFactory *f : ToolChainFactory::allToolChainFactories()) { + QElapsedTimer et; + et.start(); result.append(f->autoDetect(detector)); + qCDebug(Log) << f->displayName() << "auto detection took: " << et.elapsed() << "ms"; + } // Remove invalid toolchains that might have sneaked in. return Utils::filtered(result, [](const ToolChain *tc) { return tc->isValid(); }); From d22fba5d7fbe36f2d9199c1668d51e987f2a3d46 Mon Sep 17 00:00:00 2001 From: Ari Parkkila Date: Wed, 12 Oct 2022 14:37:14 +0300 Subject: [PATCH 024/143] Enable deploy step via rsync for Boot2Qt target Fixes: QTCREATORBUG-24731 Change-Id: I1a4aaa9f653d42e89488b1790d9ddd10d3fb49dc Reviewed-by: hjk --- src/plugins/boot2qt/qdbplugin.cpp | 3 + src/plugins/remotelinux/remotelinuxplugin.cpp | 2 +- src/plugins/remotelinux/rsyncdeploystep.cpp | 78 +++++++++---------- src/plugins/remotelinux/rsyncdeploystep.h | 16 ++-- 4 files changed, 53 insertions(+), 46 deletions(-) diff --git a/src/plugins/boot2qt/qdbplugin.cpp b/src/plugins/boot2qt/qdbplugin.cpp index cc01f0f9587..a1157cca01a 100644 --- a/src/plugins/boot2qt/qdbplugin.cpp +++ b/src/plugins/boot2qt/qdbplugin.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -156,6 +157,8 @@ public: QdbDeployStepFactory m_directUploadStepFactory{RemoteLinux::Constants::DirectUploadStepId}; + QdbDeployStepFactory + m_rsyncDeployStepFactory{RemoteLinux::Constants::RsyncDeployStepId}; QdbDeployStepFactory m_makeInstallStepFactory{RemoteLinux::Constants::MakeInstallStepId}; diff --git a/src/plugins/remotelinux/remotelinuxplugin.cpp b/src/plugins/remotelinux/remotelinuxplugin.cpp index 4da224be93a..5034eb908cf 100644 --- a/src/plugins/remotelinux/remotelinuxplugin.cpp +++ b/src/plugins/remotelinux/remotelinuxplugin.cpp @@ -58,7 +58,7 @@ public: TarPackageCreationStepFactory tarPackageCreationStepFactory; TarPackageDeployStepFactory tarPackageDeployStepFactory; GenericDeployStepFactory genericDirectUploadStepFactory; - RsyncDeployStepFactory rsyncDeployStepFactory; + GenericDeployStepFactory rsyncDeployStepFactory; CustomCommandDeployStepFactory customCommandDeployStepFactory; KillAppStepFactory killAppStepFactory; GenericDeployStepFactory makeInstallStepFactory; diff --git a/src/plugins/remotelinux/rsyncdeploystep.cpp b/src/plugins/remotelinux/rsyncdeploystep.cpp index 29985a1c3f6..ddd9079c125 100644 --- a/src/plugins/remotelinux/rsyncdeploystep.cpp +++ b/src/plugins/remotelinux/rsyncdeploystep.cpp @@ -23,7 +23,7 @@ using namespace ProjectExplorer; using namespace Utils; -namespace RemoteLinux::Internal { +namespace RemoteLinux { class RsyncDeployService : public AbstractRemoteLinuxDeployService { @@ -135,46 +135,44 @@ void RsyncDeployService::setFinished() // RsyncDeployStep -class RsyncDeployStep : public AbstractRemoteLinuxDeployStep -{ -public: - RsyncDeployStep(BuildStepList *bsl, Id id) +RsyncDeployStep::RsyncDeployStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id) - { - auto service = createDeployService(); - - auto flags = addAspect(); - flags->setDisplayStyle(StringAspect::LineEditDisplay); - flags->setSettingsKey("RemoteLinux.RsyncDeployStep.Flags"); - flags->setLabelText(Tr::tr("Flags:")); - flags->setValue(FileTransferSetupData::defaultRsyncFlags()); - - auto ignoreMissingFiles = addAspect(); - ignoreMissingFiles->setSettingsKey("RemoteLinux.RsyncDeployStep.IgnoreMissingFiles"); - ignoreMissingFiles->setLabel(Tr::tr("Ignore missing files:"), - BoolAspect::LabelPlacement::InExtraLabel); - ignoreMissingFiles->setValue(false); - - setInternalInitializer([service, flags, ignoreMissingFiles] { - service->setIgnoreMissingFiles(ignoreMissingFiles->value()); - service->setFlags(flags->value()); - return service->isDeploymentPossible(); - }); - - setRunPreparer([this, service] { - service->setDeployableFiles(target()->deploymentData().allFiles()); - }); - } -}; - -// RsyncDeployStepFactory - -RsyncDeployStepFactory::RsyncDeployStepFactory() { - registerStep(Constants::RsyncDeployStepId); - setDisplayName(Tr::tr("Deploy files via rsync")); - setSupportedConfiguration(RemoteLinux::Constants::DeployToGenericLinux); - setSupportedStepList(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY); + auto service = createDeployService(); + + auto flags = addAspect(); + flags->setDisplayStyle(StringAspect::LineEditDisplay); + flags->setSettingsKey("RemoteLinux.RsyncDeployStep.Flags"); + flags->setLabelText(Tr::tr("Flags:")); + flags->setValue(FileTransferSetupData::defaultRsyncFlags()); + + auto ignoreMissingFiles = addAspect(); + ignoreMissingFiles->setSettingsKey("RemoteLinux.RsyncDeployStep.IgnoreMissingFiles"); + ignoreMissingFiles->setLabel(Tr::tr("Ignore missing files:"), + BoolAspect::LabelPlacement::InExtraLabel); + ignoreMissingFiles->setValue(false); + + setInternalInitializer([service, flags, ignoreMissingFiles] { + service->setIgnoreMissingFiles(ignoreMissingFiles->value()); + service->setFlags(flags->value()); + return service->isDeploymentPossible(); + }); + + setRunPreparer([this, service] { + service->setDeployableFiles(target()->deploymentData().allFiles()); + }); } -} // RemoteLinux::Internal +RsyncDeployStep::~RsyncDeployStep() = default; + +Utils::Id RsyncDeployStep::stepId() +{ + return Constants::RsyncDeployStepId; +} + +QString RsyncDeployStep::displayName() +{ + return Tr::tr("Deploy files via rsync"); +} + +} // RemoteLinux diff --git a/src/plugins/remotelinux/rsyncdeploystep.h b/src/plugins/remotelinux/rsyncdeploystep.h index 7816c58ba29..5775fb5bde2 100644 --- a/src/plugins/remotelinux/rsyncdeploystep.h +++ b/src/plugins/remotelinux/rsyncdeploystep.h @@ -3,14 +3,20 @@ #pragma once -#include +#include "remotelinux_export.h" -namespace RemoteLinux::Internal { +#include "abstractremotelinuxdeploystep.h" -class RsyncDeployStepFactory : public ProjectExplorer::BuildStepFactory +namespace RemoteLinux { + +class REMOTELINUX_EXPORT RsyncDeployStep : public AbstractRemoteLinuxDeployStep { public: - RsyncDeployStepFactory(); + RsyncDeployStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id); + ~RsyncDeployStep() override; + + static Utils::Id stepId(); + static QString displayName(); }; -} // RemoteLinux::Internal +} // namespace RemoteLinux From 871aeed8ebfdc13e0ec3b859d65db9ef113bbaf8 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 12 Oct 2022 16:55:36 +0200 Subject: [PATCH 025/143] Bump version to 9.0.0-beta2 Change-Id: I2ad221b1f6eef849220de2870ac2219e5c008279 Reviewed-by: David Schulz Reviewed-by: --- cmake/QtCreatorIDEBranding.cmake | 6 +++--- qbs/modules/qtc/qtc.qbs | 6 +++--- qtcreator_ide_branding.pri | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cmake/QtCreatorIDEBranding.cmake b/cmake/QtCreatorIDEBranding.cmake index 4a72af38ad5..2b9a50628a4 100644 --- a/cmake/QtCreatorIDEBranding.cmake +++ b/cmake/QtCreatorIDEBranding.cmake @@ -1,6 +1,6 @@ -set(IDE_VERSION "8.0.82") # The IDE version. -set(IDE_VERSION_COMPAT "8.0.82") # The IDE Compatibility version. -set(IDE_VERSION_DISPLAY "9.0.0-beta1") # The IDE display version. +set(IDE_VERSION "8.0.83") # The IDE version. +set(IDE_VERSION_COMPAT "8.0.83") # The IDE Compatibility version. +set(IDE_VERSION_DISPLAY "9.0.0-beta2") # The IDE display version. set(IDE_COPYRIGHT_YEAR "2022") # The IDE current copyright year. set(IDE_SETTINGSVARIANT "QtProject") # The IDE settings variation. diff --git a/qbs/modules/qtc/qtc.qbs b/qbs/modules/qtc/qtc.qbs index 91528983f62..dbf9a7c9924 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: '9.0.0-beta1' + property string qtcreator_display_version: '9.0.0-beta2' property string ide_version_major: '8' property string ide_version_minor: '0' - property string ide_version_release: '82' + property string ide_version_release: '83' property string qtcreator_version: ide_version_major + '.' + ide_version_minor + '.' + ide_version_release property string ide_compat_version_major: '8' property string ide_compat_version_minor: '0' - property string ide_compat_version_release: '82' + property string ide_compat_version_release: '83' property string qtcreator_compat_version: ide_compat_version_major + '.' + ide_compat_version_minor + '.' + ide_compat_version_release diff --git a/qtcreator_ide_branding.pri b/qtcreator_ide_branding.pri index 7315e3b6750..1d81a2c16f0 100644 --- a/qtcreator_ide_branding.pri +++ b/qtcreator_ide_branding.pri @@ -1,6 +1,6 @@ -QTCREATOR_VERSION = 8.0.82 -QTCREATOR_COMPAT_VERSION = 8.0.82 -QTCREATOR_DISPLAY_VERSION = 9.0.0-beta1 +QTCREATOR_VERSION = 8.0.83 +QTCREATOR_COMPAT_VERSION = 8.0.83 +QTCREATOR_DISPLAY_VERSION = 9.0.0-beta2 QTCREATOR_COPYRIGHT_YEAR = 2022 IDE_DISPLAY_NAME = Qt Creator From c319163e667e2ea1b8d3782b1ba411fd07fa87e1 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 11 Oct 2022 11:16:58 +0200 Subject: [PATCH 026/143] build.py/CPack: Install to /opt from .deb packages Default is /usr/local, and that is also the default when installing self-built versions, so avoid a conflict. Change-Id: I92879baa4afd1b90b7de6addfbcd7ec361b396cc Reviewed-by: Cristian Adam Reviewed-by: --- scripts/build.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/build.py b/scripts/build.py index d69c6e12868..ee83021fb95 100755 --- a/scripts/build.py +++ b/scripts/build.py @@ -188,6 +188,8 @@ def build_qtcreator(args, paths): if args.with_cpack: cmake_args += ['-DCPACK_PACKAGE_FILE_NAME=qtcreator' + args.zip_infix] + if common.is_linux_platform(): + cmake_args += ['-DCPACK_INSTALL_PREFIX=/opt'] cmake_args += args.config_args From 048fd5955d1d715340b8b28f09a94518c291f865 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Wed, 12 Oct 2022 08:59:21 +0200 Subject: [PATCH 027/143] QmlProfiler: Allow setting breakpoints in profile result line The results of a profile run are not displayed as clickable textmarks anymore but moved to the annotation area. So do not block setting breakpoints in those lines. Fixes: QTCREATORBUG-28288 Change-Id: I517ce6f16dd9e03ff24b674e6b97aa4fcb5fe404 Reviewed-by: Christian Stenger --- src/plugins/qmlprofiler/qmlprofilertextmark.cpp | 7 ------- src/plugins/qmlprofiler/qmlprofilertextmark.h | 2 -- 2 files changed, 9 deletions(-) diff --git a/src/plugins/qmlprofiler/qmlprofilertextmark.cpp b/src/plugins/qmlprofiler/qmlprofilertextmark.cpp index c35a18a8926..2775c5771bf 100644 --- a/src/plugins/qmlprofiler/qmlprofilertextmark.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertextmark.cpp @@ -34,13 +34,6 @@ void QmlProfilerTextMark::addTypeId(int typeId) setLineAnnotation(statisticsView->summary(m_typeIds)); } -void QmlProfilerTextMark::clicked() -{ - int typeId = m_typeIds.takeFirst(); - m_typeIds.append(typeId); - emit m_viewManager->typeSelected(typeId); -} - QmlProfilerTextMarkModel::QmlProfilerTextMarkModel(QObject *parent) : QObject(parent) { } diff --git a/src/plugins/qmlprofiler/qmlprofilertextmark.h b/src/plugins/qmlprofiler/qmlprofilertextmark.h index de049afbf24..75ed95ded76 100644 --- a/src/plugins/qmlprofiler/qmlprofilertextmark.h +++ b/src/plugins/qmlprofiler/qmlprofilertextmark.h @@ -17,8 +17,6 @@ public: const Utils::FilePath &fileName, int lineNumber); void addTypeId(int typeId); - void clicked() override; - bool isClickable() const override { return true; } bool addToolTipContent(QLayout *target) const override; private: From 20cd55046fb6f0a5124ebd5c85c37307d4e7fa60 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Thu, 13 Oct 2022 09:11:59 +0300 Subject: [PATCH 028/143] VCS: Do not prompt on close if nothing was done MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: QTCREATORBUG-22233 Change-Id: I1e8240131ccb9a6583a8ae76b3f5c93799f588a5 Reviewed-by: André Hartmann --- src/plugins/vcsbase/submiteditorwidget.cpp | 5 +++++ src/plugins/vcsbase/submiteditorwidget.h | 1 + src/plugins/vcsbase/vcsbasesubmiteditor.cpp | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/vcsbase/submiteditorwidget.cpp b/src/plugins/vcsbase/submiteditorwidget.cpp index 4d01d1f549f..4a4b806915d 100644 --- a/src/plugins/vcsbase/submiteditorwidget.cpp +++ b/src/plugins/vcsbase/submiteditorwidget.cpp @@ -645,6 +645,11 @@ bool SubmitEditorWidget::canSubmit(QString *whyNot) const return res; } +bool SubmitEditorWidget::edited() const +{ + return !d->m_description.trimmed().isEmpty() || checkedFilesCount() > 0; +} + void SubmitEditorWidget::setUpdateInProgress(bool value) { d->m_updateInProgress = value; diff --git a/src/plugins/vcsbase/submiteditorwidget.h b/src/plugins/vcsbase/submiteditorwidget.h index 63fd8337104..2f0a8f8a070 100644 --- a/src/plugins/vcsbase/submiteditorwidget.h +++ b/src/plugins/vcsbase/submiteditorwidget.h @@ -67,6 +67,7 @@ public: QList submitFieldWidgets() const; virtual bool canSubmit(QString *whyNot = nullptr) const; + bool edited() const; void setUpdateInProgress(bool value); bool updateInProgress() const; diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp index 90613a4167f..a11b9b4375c 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp @@ -509,7 +509,7 @@ VcsBaseSubmitEditor::PromptSubmitResult Core::EditorManager::activateEditor(this, Core::EditorManager::IgnoreNavigationHistory); - if (!submitWidget->isEnabled()) + if (!submitWidget->isEnabled() || !submitWidget->edited()) return SubmitDiscarded; QString errorMessage; From 07f040bd1f107eed05cd9fba4c7454c4f7c59ddf Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Thu, 13 Oct 2022 08:54:24 +0200 Subject: [PATCH 029/143] Utils: Allow [] wildcard when using find Change-Id: Idc8074b85213c6d301279a01cd1b838584a66133 Reviewed-by: hjk --- src/libs/utils/filepath.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 22d4d9b42cc..bdc194b376e 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -1938,14 +1938,8 @@ QStringList FileFilter::asFindArguments(const QString &path) const const QString nameOption = (filters & QDir::CaseSensitive) ? QString{"-name"} : QString{"-iname"}; if (!nameFilters.isEmpty()) { - const QRegularExpression oneChar("\\[.*?\\]"); bool addedFirst = false; for (const QString ¤t : nameFilters) { - if (current.indexOf(oneChar) != -1) { - qDebug() << "Skipped" << current << "due to presence of [] wildcard"; - continue; - } - if (addedFirst) filterOptions << "-o"; filterOptions << nameOption << current; From 6c858c5cd6cf4e3a5081c8e547bba8f0beb3986d Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Thu, 13 Oct 2022 08:55:50 +0200 Subject: [PATCH 030/143] Utils: Fix find name operator precedence Change-Id: I553656c3aaa96230a6be60fdff69e90be03684be Reviewed-by: Eike Ziller --- src/libs/utils/filepath.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index bdc194b376e..91796a6850f 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -1938,13 +1938,15 @@ QStringList FileFilter::asFindArguments(const QString &path) const const QString nameOption = (filters & QDir::CaseSensitive) ? QString{"-name"} : QString{"-iname"}; if (!nameFilters.isEmpty()) { - bool addedFirst = false; + bool isFirst = true; + filterOptions << "("; for (const QString ¤t : nameFilters) { - if (addedFirst) + if (!isFirst) filterOptions << "-o"; filterOptions << nameOption << current; - addedFirst = true; + isFirst = false; } + filterOptions << ")"; } arguments << filterOptions; return arguments; From 35d0e9dea854ce1e40b23a9ff9dcaf1be8893365 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 13 Oct 2022 08:53:37 +0200 Subject: [PATCH 031/143] CMake: Make configure environment optically part of configure step Change-Id: I1b246acb2a057e624f7808d385a2d33c716d535d Reviewed-by: Alessandro Portale --- .../cmakebuildconfiguration.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 7dd6dc2f973..79bb9925cc6 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -175,14 +175,10 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : BuildConfiguration *bc = bs->buildConfiguration(); CMakeBuildConfiguration *cbc = static_cast(bc); - auto vbox = new QVBoxLayout(this); - vbox->setContentsMargins(0, 0, 0, 0); m_configureDetailsWidget = new DetailsWidget; updateConfigureDetailsWidgetsSummary(); - vbox->addWidget(m_configureDetailsWidget); - auto details = new QWidget(m_configureDetailsWidget); m_configureDetailsWidget->setWidget(details); @@ -333,9 +329,6 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : envWidget->setBaseEnvironmentText(cbc->baseConfigureEnvironmentText()); }); - vbox->addWidget(clearBox); - vbox->addWidget(envWidget); - using namespace Layouting; Grid cmakeConfiguration { m_filterEdit, br, @@ -372,10 +365,16 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : }, m_reconfigureButton, } - } + }, + clearBox, + envWidget }.setSpacing(0) }.attachTo(details, WithoutMargins); + Column { + m_configureDetailsWidget, + }.attachTo(this, WithoutMargins); + updateAdvancedCheckBox(); setError(m_buildSystem->error()); setWarning(m_buildSystem->warning()); From e0832ce7fca490ec1656933ce5bedb6d6da969cd Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Thu, 13 Oct 2022 11:03:09 +0200 Subject: [PATCH 032/143] CMakeProjectManager: Default spacing in CMakeBuildConfiguration UI Change-Id: Ida08086c4632d28f5c79e6c0351bf36125afee36 Reviewed-by: hjk --- src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 79bb9925cc6..3dc13248b41 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -368,7 +368,7 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) : }, clearBox, envWidget - }.setSpacing(0) + } }.attachTo(details, WithoutMargins); Column { From 348bad2fdb1c99cef37cdef2c6c02950e39f6499 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 12 Oct 2022 20:54:24 +0300 Subject: [PATCH 033/143] QmlDesigner: Fix assets library drag glitch When clicking an asset and then moving the mouse, drag is still active. Happens on mac and linux only. Change-Id: Ic61a9316013271e8e16a30629416787c863840a3 Reviewed-by: Miikka Heikkinen --- .../components/assetslibrary/assetslibrarywidget.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 7c111f80c0b..93057e6733a 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -91,6 +91,8 @@ bool AssetsLibraryWidget::eventFilter(QObject *obj, QEvent *event) m_assetsToDrag.clear(); } } + } else if (event->type() == QMouseEvent::MouseButtonRelease) { + m_assetsToDrag.clear(); } return QObject::eventFilter(obj, event); From 1fa32552425f0c3fd98ae20bfd26b8b85bfda3e0 Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 10 Oct 2022 17:32:56 +0200 Subject: [PATCH 034/143] Utils: Split off file access interface from IDevice The file accessing functions form now a class hierarchy by themselves, the devices return a suitable point. The previous implementation was mildly confusing by the special handling of the DesktopDevice, fallbacks and remote cases in the same function leading to unnecessary boilerplate when adding new functions and codepaths that sometimes passed the FilePath API twice. Implemented are a "DesktopDeviceFileAccess" taking care of the previous !needsDevice() branches and a "UnixDeviceFileAccess" covering the current docker and RL uses. As a side-effect this unifies to a large degree the current docker and RL code paths with were occasionally deviating from each other while they shouldn't. Change-Id: I4ff59d4be2a07d13e2ca5e9ace26a84160a87c9d Reviewed-by: Jarek Kobus --- src/libs/utils/CMakeLists.txt | 1 + src/libs/utils/devicefileaccess.cpp | 862 ++++++++++++++++++ src/libs/utils/devicefileaccess.h | 194 ++++ src/libs/utils/filepath.cpp | 370 ++------ src/libs/utils/filepath.h | 50 +- src/libs/utils/fileutils.cpp | 154 ---- src/libs/utils/fileutils.h | 7 - src/libs/utils/utils.qbs | 2 + src/plugins/docker/dockerdevice.cpp | 261 +----- src/plugins/docker/dockerdevice.h | 31 - .../devicesupport/desktopdevice.cpp | 143 +-- .../devicesupport/desktopdevice.h | 29 - .../devicesupport/devicemanager.cpp | 186 +--- .../projectexplorer/devicesupport/idevice.cpp | 234 +---- .../projectexplorer/devicesupport/idevice.h | 47 +- src/plugins/projectexplorer/gcctoolchain.cpp | 3 +- src/plugins/remotelinux/linuxdevice.cpp | 248 +---- src/plugins/remotelinux/linuxdevice.h | 31 +- src/tools/sdktool/CMakeLists.txt | 1 + src/tools/sdktool/sdktoollib.qbs | 1 + tests/auto/utils/fsengine/tst_fsengine.cpp | 115 +-- 21 files changed, 1233 insertions(+), 1737 deletions(-) create mode 100644 src/libs/utils/devicefileaccess.cpp create mode 100644 src/libs/utils/devicefileaccess.h diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt index 1d7099bc3a9..a4135401651 100644 --- a/src/libs/utils/CMakeLists.txt +++ b/src/libs/utils/CMakeLists.txt @@ -28,6 +28,7 @@ add_qtc_library(Utils delegates.cpp delegates.h detailsbutton.cpp detailsbutton.h detailswidget.cpp detailswidget.h + devicefileaccess.cpp devicefileaccess.h deviceshell.cpp deviceshell.h differ.cpp differ.h displayname.cpp displayname.h diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp new file mode 100644 index 00000000000..6b07edaf62b --- /dev/null +++ b/src/libs/utils/devicefileaccess.cpp @@ -0,0 +1,862 @@ +// 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 "devicefileaccess.h" + +#include "algorithm.h" +#include "qtcassert.h" +#include "hostosinfo.h" + +#include +#include +#include + +namespace Utils { + +// DeviceFileAccess + +DeviceFileAccess::~DeviceFileAccess() = default; + +QString DeviceFileAccess::mapToDevicePath(const FilePath &filePath) const +{ + return filePath.path(); +} + +bool DeviceFileAccess::isExecutableFile(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::isReadableFile(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::isWritableFile(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::isReadableDirectory(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::isWritableDirectory(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::isFile(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::isDirectory(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const +{ + if (isWritableDirectory(filePath)) + return true; + return createDirectory(filePath); +} + +bool DeviceFileAccess::ensureExistingFile(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::createDirectory(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::exists(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::removeFile(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::removeRecursively(const FilePath &filePath, QString *error) const +{ + Q_UNUSED(filePath) + Q_UNUSED(error) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const +{ + Q_UNUSED(filePath) + Q_UNUSED(target) + QTC_CHECK(false); + return false; +} + +bool DeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const +{ + Q_UNUSED(filePath) + Q_UNUSED(target) + QTC_CHECK(false); + return false; +} + +OsType DeviceFileAccess::osType(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + return OsTypeOther; +} + +FilePath DeviceFileAccess::symLinkTarget(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return {}; +} + +void DeviceFileAccess::iterateDirectory( + const FilePath &filePath, + const FilePath::IterateDirCallback &callBack, + const FileFilter &filter) const +{ + Q_UNUSED(filePath) + Q_UNUSED(callBack) + Q_UNUSED(filter) + QTC_CHECK(false); +} + +std::optional DeviceFileAccess::fileContents( + const FilePath &filePath, + qint64 limit, + qint64 offset) const +{ + Q_UNUSED(filePath) + Q_UNUSED(limit) + Q_UNUSED(offset) + QTC_CHECK(false); + return {}; +} + +bool DeviceFileAccess::writeFileContents( + const FilePath &filePath, + const QByteArray &data, + qint64 offset) const +{ + Q_UNUSED(filePath) + Q_UNUSED(data) + Q_UNUSED(offset) + QTC_CHECK(false); + return false; +} + +FilePathInfo DeviceFileAccess::filePathInfo(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return {}; +} + +QDateTime DeviceFileAccess::lastModified(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return {}; +} + +QFile::Permissions DeviceFileAccess::permissions(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return {}; +} + +bool DeviceFileAccess::setPermissions(const FilePath &filePath, QFile::Permissions) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +qint64 DeviceFileAccess::fileSize(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + +qint64 DeviceFileAccess::bytesAvailable(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return -1; +} + +void DeviceFileAccess::asyncFileContents( + const FilePath &filePath, + const Continuation> &cont, + qint64 limit, + qint64 offset) const +{ + cont(fileContents(filePath, limit, offset)); +} + +void DeviceFileAccess::asyncWriteFileContents( + const FilePath &filePath, + const Continuation &cont, + const QByteArray &data, + qint64 offset) const +{ + cont(writeFileContents(filePath, data, offset)); +} + +void DeviceFileAccess::asyncCopyFile( + const FilePath &filePath, + const Continuation &cont, + const FilePath &target) const +{ + cont(copyFile(filePath, target)); +} + + +// DesktopDeviceFileAccess + +DesktopDeviceFileAccess::~DesktopDeviceFileAccess() = default; + +DesktopDeviceFileAccess *DesktopDeviceFileAccess::instance() +{ + static DesktopDeviceFileAccess theInstance; + return &theInstance; +} + +bool DesktopDeviceFileAccess::isExecutableFile(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + return fi.isExecutable() && !fi.isDir(); +} + +bool DesktopDeviceFileAccess::isReadableFile(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + return fi.exists() && fi.isFile() && fi.isReadable(); +} + +bool DesktopDeviceFileAccess::isWritableFile(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + return fi.exists() && fi.isFile() && fi.isWritable(); +} + +bool DesktopDeviceFileAccess::isReadableDirectory(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + return fi.exists() && fi.isDir() && fi.isReadable(); +} + +bool DesktopDeviceFileAccess::isWritableDirectory(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + return fi.exists() && fi.isDir() && fi.isWritable(); +} + +bool DesktopDeviceFileAccess::isFile(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + return fi.isFile(); +} + +bool DesktopDeviceFileAccess::isDirectory(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + return fi.isDir(); +} + +bool DesktopDeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + if (fi.isDir() && fi.isWritable()) + return true; + return QDir().mkpath(filePath.path()); +} + +bool DesktopDeviceFileAccess::ensureExistingFile(const FilePath &filePath) const +{ + QFile f(filePath.path()); + if (f.exists()) + return true; + f.open(QFile::WriteOnly); + f.close(); + return f.exists(); +} + +bool DesktopDeviceFileAccess::createDirectory(const FilePath &filePath) const +{ + QDir dir(filePath.path()); + return dir.mkpath(dir.absolutePath()); +} + +bool DesktopDeviceFileAccess::exists(const FilePath &filePath) const +{ + return !filePath.isEmpty() && QFileInfo::exists(filePath.path()); +} + +bool DesktopDeviceFileAccess::removeFile(const FilePath &filePath) const +{ + return QFile::remove(filePath.path()); +} + +bool DesktopDeviceFileAccess::removeRecursively(const FilePath &filePath, QString *error) const +{ + QTC_ASSERT(!filePath.needsDevice(), return false); + QFileInfo fileInfo = filePath.toFileInfo(); + if (!fileInfo.exists() && !fileInfo.isSymLink()) + return true; + + QFile::setPermissions(fileInfo.absoluteFilePath(), fileInfo.permissions() | QFile::WriteUser); + + if (fileInfo.isDir()) { + QDir dir(fileInfo.absoluteFilePath()); + dir.setPath(dir.canonicalPath()); + if (dir.isRoot()) { + if (error) { + *error = QCoreApplication::translate("Utils::FileUtils", + "Refusing to remove root directory."); + } + return false; + } + if (dir.path() == QDir::home().canonicalPath()) { + if (error) { + *error = QCoreApplication::translate("Utils::FileUtils", + "Refusing to remove your home directory."); + } + return false; + } + + const QStringList fileNames = dir.entryList( + QDir::Files | QDir::Hidden | QDir::System | QDir::Dirs | QDir::NoDotAndDotDot); + for (const QString &fileName : fileNames) { + if (!removeRecursively(filePath / fileName, error)) + return false; + } + if (!QDir::root().rmdir(dir.path())) { + if (error) { + *error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove directory \"%1\".") + .arg(filePath.toUserOutput()); + } + return false; + } + } else { + if (!QFile::remove(filePath.toString())) { + if (error) { + *error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove file \"%1\".") + .arg(filePath.toUserOutput()); + } + return false; + } + } + return true; +} + +bool DesktopDeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const +{ + return QFile::copy(filePath.path(), target.path()); +} + +bool DesktopDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const +{ + return QFile::rename(filePath.path(), target.path()); +} + +FilePathInfo DesktopDeviceFileAccess::filePathInfo(const FilePath &filePath) const +{ + FilePathInfo result; + + QFileInfo fi(filePath.path()); + result.fileSize = fi.size(); + result.lastModified = fi.lastModified(); + result.fileFlags = (FilePathInfo::FileFlag) int(fi.permissions()); + + if (fi.isDir()) + result.fileFlags |= FilePathInfo::DirectoryType; + if (fi.isFile()) + result.fileFlags |= FilePathInfo::FileType; + if (fi.exists()) + result.fileFlags |= FilePathInfo::ExistsFlag; + if (fi.isSymbolicLink()) + result.fileFlags |= FilePathInfo::LinkType; + if (fi.isBundle()) + result.fileFlags |= FilePathInfo::BundleType; + if (fi.isHidden()) + result.fileFlags |= FilePathInfo::HiddenFlag; + if (fi.isRoot()) + result.fileFlags |= FilePathInfo::RootFlag; + + return result; +} + +FilePath DesktopDeviceFileAccess::symLinkTarget(const FilePath &filePath) const +{ + const QFileInfo info(filePath.path()); + if (!info.isSymLink()) + return {}; + return FilePath::fromString(info.symLinkTarget()); +} + +void DesktopDeviceFileAccess::iterateDirectory( + const FilePath &filePath, + const FilePath::IterateDirCallback &callBack, + const FileFilter &filter) const +{ + QDirIterator it(filePath.path(), filter.nameFilters, filter.fileFilters, filter.iteratorFlags); + while (it.hasNext()) { + const FilePath path = FilePath::fromString(it.next()); + bool res = false; + if (callBack.index() == 0) + res = std::get<0>(callBack)(path); + else + res = std::get<1>(callBack)(path, path.filePathInfo()); + if (!res) + return; + } +} + +std::optional DesktopDeviceFileAccess::fileContents( + const FilePath &filePath, + qint64 limit, + qint64 offset) const +{ + const QString path = filePath.path(); + QFile f(path); + if (!f.exists()) + return {}; + + if (!f.open(QFile::ReadOnly)) + return {}; + + if (offset != 0) + f.seek(offset); + + if (limit != -1) + return f.read(limit); + + return f.readAll(); +} + +bool DesktopDeviceFileAccess::writeFileContents( + const FilePath &filePath, + const QByteArray &data, + qint64 offset) const +{ + QFile file(filePath.path()); + QTC_ASSERT(file.open(QFile::WriteOnly | QFile::Truncate), return false); + if (offset != 0) + file.seek(offset); + qint64 res = file.write(data); + return res == data.size(); +} + +QDateTime DesktopDeviceFileAccess::lastModified(const FilePath &filePath) const +{ + return QFileInfo(filePath.path()).lastModified(); +} + +QFile::Permissions DesktopDeviceFileAccess::permissions(const FilePath &filePath) const +{ + return QFileInfo(filePath.path()).permissions(); +} + +bool DesktopDeviceFileAccess::setPermissions(const FilePath &filePath, + QFile::Permissions permissions) const +{ + return QFile(filePath.path()).setPermissions(permissions); +} + +qint64 DesktopDeviceFileAccess::fileSize(const FilePath &filePath) const +{ + return QFileInfo(filePath.path()).size(); +} + +qint64 DesktopDeviceFileAccess::bytesAvailable(const FilePath &filePath) const +{ + return QStorageInfo(filePath.path()).bytesAvailable(); +} + +OsType DesktopDeviceFileAccess::osType(const FilePath &filePath) const +{ + Q_UNUSED(filePath); + return HostOsInfo::hostOs(); +} + + +// UnixDeviceAccess + +UnixDeviceFileAccess::~UnixDeviceFileAccess() = default; + +bool UnixDeviceFileAccess::runInShellSuccess( + const QString &executable, + const QStringList &args, + const QByteArray &stdInData) const +{ + return runInShell(executable, args, stdInData).exitCode == 0; +} + +bool UnixDeviceFileAccess::isExecutableFile(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-x", path}); +} + +bool UnixDeviceFileAccess::isReadableFile(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-r", path, "-a", "-f", path}); +} + +bool UnixDeviceFileAccess::isWritableFile(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-w", path, "-a", "-f", path}); +} + +bool UnixDeviceFileAccess::isReadableDirectory(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-r", path, "-a", "-d", path}); +} + +bool UnixDeviceFileAccess::isWritableDirectory(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-w", path, "-a", "-d", path}); +} + +bool UnixDeviceFileAccess::isFile(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-f", path}); +} + +bool UnixDeviceFileAccess::isDirectory(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-d", path}); +} + +bool UnixDeviceFileAccess::ensureExistingFile(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("touch", {path}); +} + +bool UnixDeviceFileAccess::createDirectory(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("mkdir", {"-p", path}); +} + +bool UnixDeviceFileAccess::exists(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-e", path}); +} + +bool UnixDeviceFileAccess::removeFile(const FilePath &filePath) const +{ + return runInShellSuccess("rm", {filePath.path()}); +} + +bool UnixDeviceFileAccess::removeRecursively(const FilePath &filePath, QString *error) const +{ + QTC_ASSERT(filePath.path().startsWith('/'), return false); + + const QString path = filePath.cleanPath().path(); + // We are expecting this only to be called in a context of build directories or similar. + // Chicken out in some cases that _might_ be user code errors. + QTC_ASSERT(path.startsWith('/'), return false); + int levelsNeeded = path.startsWith("/home/") ? 4 : 3; + if (path.startsWith("/tmp/")) + levelsNeeded = 2; + QTC_ASSERT(path.count('/') >= levelsNeeded, return false); + + RunResult result = runInShell("rm", {"-rf", "--", path}); + if (error) + *error = QString::fromUtf8(result.stdErr); + return result.exitCode == 0; +} + +bool UnixDeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const +{ + return runInShellSuccess("cp", {filePath.path(), target.path()}); +} + +bool UnixDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const +{ + return runInShellSuccess("mv", {filePath.path(), target.path()}); +} + +FilePath UnixDeviceFileAccess::symLinkTarget(const FilePath &filePath) const +{ + const RunResult result = runInShell("readlink", {"-n", "-e", filePath.path()}); + const QString out = QString::fromUtf8(result.stdOut); + return out.isEmpty() ? FilePath() : filePath.withNewPath(out); +} + +std::optional UnixDeviceFileAccess::fileContents( + const FilePath &filePath, + qint64 limit, + qint64 offset) const +{ + QStringList args = {"if=" + filePath.path(), "status=none"}; + if (limit > 0 || offset > 0) { + const qint64 gcd = std::gcd(limit, offset); + args += QString("bs=%1").arg(gcd); + args += QString("count=%1").arg(limit / gcd); + args += QString("seek=%1").arg(offset / gcd); + } + + const RunResult r = runInShell("dd", args); + + if (r.exitCode != 0) + return {}; + + return r.stdOut; +} + +bool UnixDeviceFileAccess::writeFileContents( + const FilePath &filePath, + const QByteArray &data, + qint64 offset) const +{ + QStringList args = {"of=" + filePath.path()}; + if (offset != 0) { + args.append("bs=1"); + args.append(QString("seek=%1").arg(offset)); + } + return runInShellSuccess("dd", args, data); +} + +OsType UnixDeviceFileAccess::osType(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + return OsTypeLinux; +} + +QDateTime UnixDeviceFileAccess::lastModified(const FilePath &filePath) const +{ + const RunResult result = runInShell("stat", {"-L", "-c", "%Y", filePath.path()}); + qint64 secs = result.stdOut.toLongLong(); + const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC); + return dt; +} + +QFile::Permissions UnixDeviceFileAccess::permissions(const FilePath &filePath) const +{ + const RunResult result = runInShell("stat", {"-L", "-c", "%a", filePath.path()}); + const uint bits = result.stdOut.toUInt(nullptr, 8); + QFileDevice::Permissions perm = {}; +#define BIT(n, p) if (bits & (1< bool { + if (callBack.index() == 0) + return std::get<0>(callBack)(filePath.withNewPath(entry)); + + const QString fileName = entry.mid(1, entry.lastIndexOf('\"') - 1); + const QString infos = entry.mid(fileName.length() + 3); + + const FilePathInfo fi = FileUtils::filePathInfoFromTriple(infos); + if (!fi.fileFlags) + return true; + + const FilePath fp = filePath.withNewPath(fileName); + 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" + if (entries.front() == filePath.path()) + entries.pop_front(); + + for (const QString &entry : entries) { + if (!toFilePath(entry)) + break; + } + + return true; +} + +void UnixDeviceFileAccess::findUsingLs( + const QString ¤t, + const FileFilter &filter, + QStringList *found) const +{ + const RunResult result = runInShell("ls", {"-1", "-p", "--", current}); + const QStringList entries = QString::fromUtf8(result.stdOut).split('\n', Qt::SkipEmptyParts); + for (QString entry : entries) { + const QChar last = entry.back(); + if (last == '/') { + entry.chop(1); + if (filter.iteratorFlags.testFlag(QDirIterator::Subdirectories)) + findUsingLs(current + '/' + entry, filter, found); + } + found->append(entry); + } +} + +// Used on 'ls' output on unix-like systems. +static void iterateLsOutput(const FilePath &base, + const QStringList &entries, + const FileFilter &filter, + const FilePath::IterateDirCallback &callBack) +{ + const QList nameRegexps = + transform(filter.nameFilters, [](const QString &filter) { + QRegularExpression re; + re.setPattern(QRegularExpression::wildcardToRegularExpression(filter)); + QTC_CHECK(re.isValid()); + return re; + }); + + const auto nameMatches = [&nameRegexps](const QString &fileName) { + for (const QRegularExpression &re : nameRegexps) { + const QRegularExpressionMatch match = re.match(fileName); + if (match.hasMatch()) + return true; + } + return nameRegexps.isEmpty(); + }; + + // FIXME: Handle filters. For now bark on unsupported options. + QTC_CHECK(filter.fileFilters == QDir::NoFilter); + + for (const QString &entry : entries) { + if (!nameMatches(entry)) + continue; + const FilePath current = base.pathAppended(entry); + bool res = false; + if (callBack.index() == 0) + res = std::get<0>(callBack)(current); + else + res = std::get<1>(callBack)(current, current.filePathInfo()); + if (!res) + break; + } +} + +void UnixDeviceFileAccess::iterateDirectory( + const FilePath &filePath, + const FilePath::IterateDirCallback &callBack, + const FileFilter &filter) const +{ + // We try to use 'find' first, because that can filter better directly. + // Unfortunately, it's not installed on all devices by default. + 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. + } + + // if we do not have find - use ls as fallback + QStringList entries; + findUsingLs(filePath.path(), filter, &entries); + iterateLsOutput(filePath, entries, filter, callBack); +} + +} // Utils diff --git a/src/libs/utils/devicefileaccess.h b/src/libs/utils/devicefileaccess.h new file mode 100644 index 00000000000..e1ef5db1ddb --- /dev/null +++ b/src/libs/utils/devicefileaccess.h @@ -0,0 +1,194 @@ +// 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_global.h" + +#include "fileutils.h" + +namespace Utils { + +// Base class including dummy implementation usable as fallback. +class QTCREATOR_UTILS_EXPORT DeviceFileAccess +{ +public: + virtual ~DeviceFileAccess(); + +protected: + friend class FilePath; + + virtual QString mapToDevicePath(const FilePath &filePath) const; + + virtual bool isExecutableFile(const FilePath &filePath) const; + virtual bool isReadableFile(const FilePath &filePath) const; + virtual bool isWritableFile(const FilePath &filePath) const; + virtual bool isReadableDirectory(const FilePath &filePath) const; + virtual bool isWritableDirectory(const FilePath &filePath) const; + virtual bool isFile(const FilePath &filePath) const; + virtual bool isDirectory(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; + virtual bool exists(const FilePath &filePath) const; + virtual bool removeFile(const FilePath &filePath) const; + virtual bool removeRecursively(const FilePath &filePath, QString *error) const; + virtual bool copyFile(const FilePath &filePath, 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; + virtual QFile::Permissions permissions(const FilePath &filePath) const; + virtual bool setPermissions(const FilePath &filePath, QFile::Permissions) const; + virtual qint64 fileSize(const FilePath &filePath) const; + virtual qint64 bytesAvailable(const FilePath &filePath) const; + + virtual void iterateDirectory( + const FilePath &filePath, + const FilePath::IterateDirCallback &callBack, + const FileFilter &filter) const; + + virtual std::optional fileContents( + const FilePath &filePath, + qint64 limit, + qint64 offset) const; + virtual bool writeFileContents( + const FilePath &filePath, + const QByteArray &data, + qint64 offset) const; + + virtual void asyncFileContents( + const FilePath &filePath, + const Continuation> &cont, + qint64 limit, + qint64 offset) const; + + virtual void asyncWriteFileContents( + const FilePath &filePath, + const Continuation &cont, + const QByteArray &data, + qint64 offset) const; + + virtual void asyncCopyFile( + const FilePath &filePath, + const Continuation &cont, + const FilePath &target) const; +}; + +class QTCREATOR_UTILS_EXPORT DesktopDeviceFileAccess : public DeviceFileAccess +{ +public: + ~DesktopDeviceFileAccess() override; + + static DesktopDeviceFileAccess *instance(); + +protected: + bool isExecutableFile(const FilePath &filePath) const override; + bool isReadableFile(const FilePath &filePath) const override; + bool isWritableFile(const FilePath &filePath) const override; + bool isReadableDirectory(const FilePath &filePath) const override; + bool isWritableDirectory(const FilePath &filePath) const override; + bool isFile(const FilePath &filePath) const override; + bool isDirectory(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; + bool exists(const FilePath &filePath) const override; + bool removeFile(const FilePath &filePath) const override; + bool removeRecursively(const FilePath &filePath, QString *error) const override; + bool 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; + QFile::Permissions permissions(const FilePath &filePath) const override; + bool setPermissions(const FilePath &filePath, QFile::Permissions) const override; + qint64 fileSize(const FilePath &filePath) const override; + qint64 bytesAvailable(const FilePath &filePath) const override; + + void iterateDirectory( + const FilePath &filePath, + const FilePath::IterateDirCallback &callBack, + const FileFilter &filter) const override; + + std::optional fileContents( + const FilePath &filePath, + qint64 limit, + qint64 offset) const override; + bool writeFileContents( + const FilePath &filePath, + const QByteArray &data, + qint64 offset) const override; +}; + +class QTCREATOR_UTILS_EXPORT UnixDeviceFileAccess : public DeviceFileAccess +{ +public: + ~UnixDeviceFileAccess() override; + +protected: + virtual RunResult runInShell( + const QString &executable, + const QStringList &args, + const QByteArray &inputData = {}) const = 0; + bool runInShellSuccess( + const QString &executable, + const QStringList &args, + const QByteArray &stdInData = {}) const; + + bool isExecutableFile(const FilePath &filePath) const override; + bool isReadableFile(const FilePath &filePath) const override; + bool isWritableFile(const FilePath &filePath) const override; + bool isReadableDirectory(const FilePath &filePath) const override; + bool isWritableDirectory(const FilePath &filePath) const override; + bool isFile(const FilePath &filePath) const override; + bool isDirectory(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; + bool removeFile(const FilePath &filePath) const override; + bool removeRecursively(const FilePath &filePath, QString *error) const override; + bool copyFile(const FilePath &filePath, const FilePath &target) const override; + 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; + bool setPermissions(const FilePath &filePath, QFile::Permissions) const override; + qint64 fileSize(const FilePath &filePath) const override; + qint64 bytesAvailable(const FilePath &filePath) const override; + + void iterateDirectory( + const FilePath &filePath, + const FilePath::IterateDirCallback &callBack, + const FileFilter &filter) const override; + + std::optional fileContents( + const FilePath &filePath, + qint64 limit, + qint64 offset) const override; + bool writeFileContents( + const FilePath &filePath, + const QByteArray &data, + qint64 offset) const override; + +private: + bool iterateWithFind( + const FilePath &filePath, + const FileFilter &filter, + const FilePath::IterateDirCallback &callBack) const; + void findUsingLs( + const QString ¤t, + const FileFilter &filter, + QStringList *found) const; + + mutable bool m_tryUseFind = true; +}; + +} // Utils diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 91796a6850f..645ded358e5 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -4,6 +4,7 @@ #include "filepath.h" #include "algorithm.h" +#include "devicefileaccess.h" #include "environment.h" #include "fileutils.h" #include "hostosinfo.h" @@ -15,7 +16,6 @@ #include #include #include -#include #include #include @@ -30,7 +30,6 @@ namespace Utils { static DeviceFileHooks s_deviceHooks; -static bool removeRecursivelyLocal(const FilePath &filePath, QString *error); inline bool isWindowsDriveLetter(QChar ch); @@ -359,136 +358,68 @@ void FilePath::setParts(const QStringView scheme, const QStringView host, const /// FilePath exists. bool FilePath::exists() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.exists, return false); - return s_deviceHooks.exists(*this); - } - return !isEmpty() && QFileInfo::exists(path()); + return fileAccess()->exists(*this); } /// \returns a bool indicating whether a path is writable. bool FilePath::isWritableDir() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.isWritableDir, return false); - return s_deviceHooks.isWritableDir(*this); - } - const QFileInfo fi{path()}; - return exists() && fi.isDir() && fi.isWritable(); + return fileAccess()->isWritableDirectory(*this); } bool FilePath::isWritableFile() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.isWritableFile, return false); - return s_deviceHooks.isWritableFile(*this); - } - const QFileInfo fi{path()}; - return fi.isWritable() && !fi.isDir(); + return fileAccess()->isWritableFile(*this); } bool FilePath::ensureWritableDir() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.ensureWritableDir, return false); - return s_deviceHooks.ensureWritableDir(*this); - } - const QFileInfo fi{path()}; - if (fi.isDir() && fi.isWritable()) - return true; - return QDir().mkpath(path()); + return fileAccess()->ensureWritableDirectory(*this); } bool FilePath::ensureExistingFile() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.ensureExistingFile, return false); - return s_deviceHooks.ensureExistingFile(*this); - } - QFile f(path()); - if (f.exists()) - return true; - f.open(QFile::WriteOnly); - f.close(); - return f.exists(); + return fileAccess()->ensureExistingFile(*this); } bool FilePath::isExecutableFile() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.isExecutableFile, return false); - return s_deviceHooks.isExecutableFile(*this); - } - const QFileInfo fi{path()}; - return fi.isExecutable() && !fi.isDir(); + return fileAccess()->isExecutableFile(*this); } bool FilePath::isReadableFile() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.isReadableFile, return false); - return s_deviceHooks.isReadableFile(*this); - } - const QFileInfo fi{path()}; - return fi.isReadable() && !fi.isDir(); + return fileAccess()->isReadableFile(*this); } bool FilePath::isReadableDir() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.isReadableDir, return false); - return s_deviceHooks.isReadableDir(*this); - } - const QFileInfo fi{path()}; - return fi.isReadable() && fi.isDir(); + return fileAccess()->isReadableDirectory(*this); } bool FilePath::isFile() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.isFile, return false); - return s_deviceHooks.isFile(*this); - } - const QFileInfo fi{path()}; - return fi.isFile(); + return fileAccess()->isFile(*this); } bool FilePath::isDir() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.isDir, return false); - return s_deviceHooks.isDir(*this); - } - const QFileInfo fi{path()}; - return fi.isDir(); + return fileAccess()->isDirectory(*this); } bool FilePath::createDir() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.createDir, return false); - return s_deviceHooks.createDir(*this); - } - QDir dir(path()); - return dir.mkpath(dir.absolutePath()); + return fileAccess()->createDirectory(*this); } FilePaths FilePath::dirEntries(const FileFilter &filter, QDir::SortFlags sort) const { FilePaths result; - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.iterateDirectory, return {}); - const auto callBack = [&result](const FilePath &path) { result.append(path); return true; }; - s_deviceHooks.iterateDirectory(*this, callBack, filter); - } else { - QDirIterator dit(path(), filter.nameFilters, filter.fileFilters, filter.iteratorFlags); - while (dit.hasNext()) - result.append(FilePath::fromString(dit.next())); - } + const auto callBack = [&result](const FilePath &path) { result.append(path); return true; }; + iterateDirectory(callBack, filter); // FIXME: Not all flags supported here. - const QDir::SortFlags sortBy = (sort & QDir::SortByMask); if (sortBy == QDir::Name) { Utils::sort(result); @@ -515,23 +446,7 @@ FilePaths FilePath::dirEntries(QDir::Filters filters) const void FilePath::iterateDirectory(const IterateDirCallback &callBack, const FileFilter &filter) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.iterateDirectory, return); - s_deviceHooks.iterateDirectory(*this, callBack, filter); - return; - } - - QDirIterator it(path(), filter.nameFilters, filter.fileFilters, filter.iteratorFlags); - while (it.hasNext()) { - const FilePath path = FilePath::fromString(it.next()); - bool res = false; - if (callBack.index() == 0) - res = std::get<0>(callBack)(path); - else - res = std::get<1>(callBack)(path, path.filePathInfo()); - if (!res) - return; - } + fileAccess()->iterateDirectory(*this, callBack, filter); } void FilePath::iterateDirectories(const FilePaths &dirs, @@ -544,26 +459,7 @@ void FilePath::iterateDirectories(const FilePaths &dirs, std::optional FilePath::fileContents(qint64 maxSize, qint64 offset) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.fileContents, return {}); - return s_deviceHooks.fileContents(*this, maxSize, offset); - } - - const QString path = toString(); - QFile f(path); - if (!f.exists()) - return {}; - - if (!f.open(QFile::ReadOnly)) - return {}; - - if (offset != 0) - f.seek(offset); - - if (maxSize != -1) - return f.read(maxSize); - - return f.readAll(); + return fileAccess()->fileContents(*this, maxSize, offset); } bool FilePath::ensureReachable(const FilePath &other) const @@ -577,78 +473,30 @@ bool FilePath::ensureReachable(const FilePath &other) const return false; } - -void FilePath::asyncFileContents(const Continuation &> &cont, - qint64 maxSize, - qint64 offset) const +void FilePath::asyncFileContents( + const Continuation &> &cont, + qint64 maxSize, + qint64 offset) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.asyncFileContents, return); - s_deviceHooks.asyncFileContents(cont, *this, maxSize, offset); - return; - } - - cont(fileContents(maxSize, offset)); + return fileAccess()->asyncFileContents(*this, cont, maxSize, offset); } bool FilePath::writeFileContents(const QByteArray &data, qint64 offset) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.writeFileContents, return {}); - return s_deviceHooks.writeFileContents(*this, data, offset); - } - - QFile file(path()); - QTC_ASSERT(file.open(QFile::WriteOnly | QFile::Truncate), return false); - if (offset != 0) - file.seek(offset); - qint64 res = file.write(data); - return res == data.size(); + return fileAccess()->writeFileContents(*this, data, offset); } FilePathInfo FilePath::filePathInfo() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.filePathInfo, return {}); - return s_deviceHooks.filePathInfo(*this); - } - - FilePathInfo result; - - QFileInfo fi(path()); - result.fileSize = fi.size(); - result.lastModified = fi.lastModified(); - result.fileFlags = (FilePathInfo::FileFlag) int(fi.permissions()); - - if (fi.isDir()) - result.fileFlags |= FilePathInfo::DirectoryType; - if (fi.isFile()) - result.fileFlags |= FilePathInfo::FileType; - if (fi.exists()) - result.fileFlags |= FilePathInfo::ExistsFlag; - if (fi.isSymbolicLink()) - result.fileFlags |= FilePathInfo::LinkType; - if (fi.isBundle()) - result.fileFlags |= FilePathInfo::BundleType; - if (fi.isHidden()) - result.fileFlags |= FilePathInfo::HiddenFlag; - if (fi.isRoot()) - result.fileFlags |= FilePathInfo::RootFlag; - - return result; + return fileAccess()->filePathInfo(*this); } -void FilePath::asyncWriteFileContents(const Continuation &cont, - const QByteArray &data, - qint64 offset) const +void FilePath::asyncWriteFileContents( + const Continuation &cont, + const QByteArray &data, + qint64 offset) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.asyncWriteFileContents, return); - s_deviceHooks.asyncWriteFileContents(cont, *this, data, offset); - return; - } - - cont(writeFileContents(data, offset)); + return fileAccess()->asyncWriteFileContents(*this, cont, data, offset); } bool FilePath::needsDevice() const @@ -670,23 +518,12 @@ bool FilePath::isSameDevice(const FilePath &other) const /// \returns an empty FilePath if this is not a symbolic linl FilePath FilePath::symLinkTarget() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.symLinkTarget, return {}); - return s_deviceHooks.symLinkTarget(*this); - } - const QFileInfo info(path()); - if (!info.isSymLink()) - return {}; - return FilePath::fromString(info.symLinkTarget()); + return fileAccess()->symLinkTarget(*this); } QString FilePath::mapToDevicePath() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.mapToDevicePath, return path()); - return s_deviceHooks.mapToDevicePath(*this); - } - return path(); + return fileAccess()->mapToDevicePath(*this); } FilePath FilePath::withExecutableSuffix() const @@ -937,6 +774,23 @@ void FilePath::setFromString(const QString &unnormalizedFileName) setParts({}, {}, fileName); } +DeviceFileAccess *FilePath::fileAccess() const +{ + if (!needsDevice()) + return DesktopDeviceFileAccess::instance(); + + if (!s_deviceHooks.fileAccess) { + // Happens during startup and in tst_fsengine + QTC_CHECK(false); + return DesktopDeviceFileAccess::instance(); + } + + static DeviceFileAccess dummy; + DeviceFileAccess *access = s_deviceHooks.fileAccess(*this); + QTC_ASSERT(access, return &dummy); + return access; +} + /// Constructs a FilePath from \a filePath. The \a defaultExtension is appended /// to \a filename if that does not have an extension already. /// \a filePath is not checked for validity. @@ -1241,10 +1095,10 @@ FilePath FilePath::withNewPath(const QString &newPath) const */ FilePath FilePath::searchInDirectories(const FilePaths &dirs) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.searchInPath, return {}); - return s_deviceHooks.searchInPath(*this, dirs); - } + if (isAbsolutePath()) + return *this; + // FIXME: Ramp down use. + QTC_ASSERT(!needsDevice(), return {}); return Environment::systemEnvironment().searchInDirectories(path(), dirs); } @@ -1252,6 +1106,7 @@ FilePath FilePath::searchInPath(const FilePaths &additionalDirs, PathAmending am { if (isAbsolutePath()) return *this; + // FIXME: Ramp down use. FilePaths directories = deviceEnvironment().path(); if (!additionalDirs.isEmpty()) { if (amending == AppendToPath) @@ -1259,7 +1114,8 @@ FilePath FilePath::searchInPath(const FilePaths &additionalDirs, PathAmending am else directories = additionalDirs + directories; } - return searchInDirectories(directories); + QTC_ASSERT(!needsDevice(), return {}); + return Environment::systemEnvironment().searchInDirectories(path(), directories); } Environment FilePath::deviceEnvironment() const @@ -1338,47 +1194,27 @@ size_t FilePath::hash(uint seed) const QDateTime FilePath::lastModified() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.lastModified, return {}); - return s_deviceHooks.lastModified(*this); - } - return toFileInfo().lastModified(); + return fileAccess()->lastModified(*this); } QFile::Permissions FilePath::permissions() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.permissions, return {}); - return s_deviceHooks.permissions(*this); - } - return toFileInfo().permissions(); + return fileAccess()->permissions(*this); } bool FilePath::setPermissions(QFile::Permissions permissions) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.setPermissions, return false); - return s_deviceHooks.setPermissions(*this, permissions); - } - return QFile(path()).setPermissions(permissions); + return fileAccess()->setPermissions(*this, permissions); } OsType FilePath::osType() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.osType, return OsType::OsTypeLinux); - return s_deviceHooks.osType(*this); - } - return HostOsInfo::hostOs(); + return fileAccess()->osType(*this); } bool FilePath::removeFile() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.removeFile, return false); - return s_deviceHooks.removeFile(*this); - } - return QFile::remove(path()); + return fileAccess()->removeFile(*this); } /*! @@ -1390,11 +1226,7 @@ bool FilePath::removeFile() const */ bool FilePath::removeRecursively(QString *error) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.removeRecursively, return false); - return s_deviceHooks.removeRecursively(*this); - } - return removeRecursivelyLocal(*this, error); + return fileAccess()->removeRecursively(*this, error); } bool FilePath::copyFile(const FilePath &target) const @@ -1406,11 +1238,7 @@ bool FilePath::copyFile(const FilePath &target) const return false; return target.writeFileContents(*ba); } - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.copyFile, return false); - return s_deviceHooks.copyFile(*this, target); - } - return QFile::copy(path(), target.path()); + return fileAccess()->copyFile(*this, target); } void FilePath::asyncCopyFile(const std::function &cont, const FilePath &target) const @@ -1420,90 +1248,24 @@ void FilePath::asyncCopyFile(const std::function &cont, const FilePa if (ba) target.asyncWriteFileContents(cont, *ba); }); - } else if (needsDevice()) { - s_deviceHooks.asyncCopyFile(cont, *this, target); - } else { - cont(copyFile(target)); + return; } + return fileAccess()->asyncCopyFile(*this, cont, target); } bool FilePath::renameFile(const FilePath &target) const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.renameFile, return false); - return s_deviceHooks.renameFile(*this, target); - } - return QFile::rename(path(), target.path()); + return fileAccess()->renameFile(*this, target); } qint64 FilePath::fileSize() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.fileSize, return false); - return s_deviceHooks.fileSize(*this); - } - return QFileInfo(path()).size(); + return fileAccess()->fileSize(*this); } qint64 FilePath::bytesAvailable() const { - if (needsDevice()) { - QTC_ASSERT(s_deviceHooks.bytesAvailable, return false); - return s_deviceHooks.bytesAvailable(*this); - } - return QStorageInfo(path()).bytesAvailable(); -} - -static bool removeRecursivelyLocal(const FilePath &filePath, QString *error) -{ - QTC_ASSERT(!filePath.needsDevice(), return false); - QFileInfo fileInfo = filePath.toFileInfo(); - if (!fileInfo.exists() && !fileInfo.isSymLink()) - return true; - - QFile::setPermissions(fileInfo.absoluteFilePath(), fileInfo.permissions() | QFile::WriteUser); - - if (fileInfo.isDir()) { - QDir dir(fileInfo.absoluteFilePath()); - dir.setPath(dir.canonicalPath()); - if (dir.isRoot()) { - if (error) { - *error = QCoreApplication::translate("Utils::FileUtils", - "Refusing to remove root directory."); - } - return false; - } - if (dir.path() == QDir::home().canonicalPath()) { - if (error) { - *error = QCoreApplication::translate("Utils::FileUtils", - "Refusing to remove your home directory."); - } - return false; - } - - const QStringList fileNames = dir.entryList( - QDir::Files | QDir::Hidden | QDir::System | QDir::Dirs | QDir::NoDotAndDotDot); - for (const QString &fileName : fileNames) { - if (!removeRecursivelyLocal(filePath / fileName, error)) - return false; - } - if (!QDir::root().rmdir(dir.path())) { - if (error) { - *error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove directory \"%1\".") - .arg(filePath.toUserOutput()); - } - return false; - } - } else { - if (!QFile::remove(filePath.toString())) { - if (error) { - *error = QCoreApplication::translate("Utils::FileUtils", "Failed to remove file \"%1\".") - .arg(filePath.toUserOutput()); - } - return false; - } - } - return true; + return fileAccess()->bytesAvailable(*this); } /*! diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h index 01100dc1320..0cc9455eef7 100644 --- a/src/libs/utils/filepath.h +++ b/src/libs/utils/filepath.h @@ -28,9 +28,12 @@ class tst_fileutils; // This becomes a friend of Utils::FilePath for testing pri namespace Utils { +class DeviceFileAccess; class Environment; class EnvironmentChange; +template using Continuation = std::function; + class QTCREATOR_UTILS_EXPORT FileFilter { public: @@ -188,7 +191,6 @@ public: static void sort(FilePaths &files); // Asynchronous interface - template using Continuation = std::function; void asyncCopyFile(const Continuation &cont, const FilePath &target) const; void asyncFileContents(const Continuation &> &cont, qint64 maxSize = -1, @@ -232,6 +234,7 @@ private: static QString calcRelativePath(const QString &absolutePath, const QString &absoluteAnchorPath); void setPath(QStringView path); void setFromString(const QString &filepath); + DeviceFileAccess *fileAccess() const; [[nodiscard]] QString mapToDevicePath() const; [[nodiscard]] QString encodedHost() const; @@ -252,50 +255,11 @@ class QTCREATOR_UTILS_EXPORT DeviceFileHooks public: static DeviceFileHooks &instance(); - std::function isExecutableFile; - std::function isReadableFile; - std::function isReadableDir; - std::function isWritableDir; - std::function isWritableFile; - std::function isFile; - std::function isDir; - std::function ensureWritableDir; - std::function ensureExistingFile; - std::function createDir; - std::function exists; - std::function removeFile; - std::function removeRecursively; - std::function copyFile; - std::function renameFile; - std::function searchInPath; - std::function symLinkTarget; - std::function mapToDevicePath; - std::function - iterateDirectory; - std::function(const FilePath &, qint64, qint64)> fileContents; - std::function writeFileContents; - std::function lastModified; - std::function permissions; - std::function setPermissions; - std::function osType; - std::function environment; - std::function fileSize; - std::function bytesAvailable; + std::function fileAccess; std::function deviceDisplayName; - std::function isSameDevice; - std::function filePathInfo; - - - template using Continuation = std::function; - std::function &, const FilePath &, const FilePath &)> asyncCopyFile; - std::function &> &, const FilePath &, qint64, qint64)> - asyncFileContents; - std::function &, const FilePath &, const QByteArray &, qint64)> - asyncWriteFileContents; std::function ensureReachable; + std::function environment; + std::function isSameDevice; }; } // namespace Utils diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index 42d8f553894..989f6a3fcc9 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -646,46 +646,6 @@ FilePaths FileUtils::getOpenFilePaths(QWidget *parent, #endif // QT_WIDGETS_LIB -// Used on 'ls' output on unix-like systems. -static void iterateLsOutput(const FilePath &base, - const QStringList &entries, - const FileFilter &filter, - const FilePath::IterateDirCallback &callBack) -{ - const QList nameRegexps = - transform(filter.nameFilters, [](const QString &filter) { - QRegularExpression re; - re.setPattern(QRegularExpression::wildcardToRegularExpression(filter)); - QTC_CHECK(re.isValid()); - return re; - }); - - const auto nameMatches = [&nameRegexps](const QString &fileName) { - for (const QRegularExpression &re : nameRegexps) { - const QRegularExpressionMatch match = re.match(fileName); - if (match.hasMatch()) - return true; - } - return nameRegexps.isEmpty(); - }; - - // FIXME: Handle filters. For now bark on unsupported options. - QTC_CHECK(filter.fileFilters == QDir::NoFilter); - - for (const QString &entry : entries) { - if (!nameMatches(entry)) - continue; - const FilePath current = base.pathAppended(entry); - bool res = false; - if (callBack.index() == 0) - res = std::get<0>(callBack)(current); - else - res = std::get<1>(callBack)(current, current.filePathInfo()); - if (!res) - break; - } -} - FilePathInfo::FileFlags fileInfoFlagsfromStatRawModeHex(const QString &hexString) { bool ok = false; @@ -741,120 +701,6 @@ FilePathInfo FileUtils::filePathInfoFromTriple(const QString &infos) return {size, flags, dt}; } -static bool iterateWithFindHelper( - const FilePath &filePath, - const FileFilter &filter, - const std::function &runInShell, - const std::function callBack, - const QString &extraArguments) -{ - QTC_CHECK(filePath.isAbsolutePath()); - const QStringList arguments = filter.asFindArguments(filePath.path()); - - CommandLine cmdLine{"find", arguments}; - if (!extraArguments.isEmpty()) - cmdLine.addArgs(extraArguments, CommandLine::Raw); - - const RunResult result = runInShell(cmdLine); - const QString out = QString::fromUtf8(result.stdOut); - if (result.exitCode != 0) { - // Find returns non-zero exit code for any error it encounters, even if it finds some files. - - if (!out.startsWith('"' + filePath.path())) { - if (!filePath.exists()) // File does not exist, so no files to find. - return true; - - // If the output does not start with the path we are searching in, find has failed. - // Possibly due to unknown options. - return false; - } - } - - QStringList entries = out.split("\n", Qt::SkipEmptyParts); - if (entries.isEmpty()) - return true; - - // Remove the first line, it is always the directory we are searching in. - // as long as we do not specify "mindepth > 0" - if (entries.size() > 0) - entries.pop_front(); - for (const QString &entry : entries) { - if (!callBack(entry)) - break; - } - - return true; -} - -// returns whether 'find' could be used. -static bool iterateWithFind( - const FilePath &filePath, - const FileFilter &filter, - const std::function &runInShell, - const FilePath::IterateDirCallback &callBack) -{ - const auto toFilePath = [&filePath, &callBack](const QString &entry) { - if (callBack.index() == 0) - return std::get<0>(callBack)(filePath.withNewPath(entry)); - - const QString fileName = entry.mid(1, entry.lastIndexOf('\"') - 1); - const QString infos = entry.mid(fileName.length() + 3); - - const FilePathInfo fi = FileUtils::filePathInfoFromTriple(infos); - if (!fi.fileFlags) - return true; - - const FilePath fp = filePath.withNewPath(fileName); - return std::get<1>(callBack)(fp, fi); - }; - - // 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. - QString infoArgs; - if (callBack.index() == 1) - infoArgs = R"(-exec echo -n \"{}\"" " \; -exec stat -L -c "%f %Y %s" "{}" \;)"; - - return iterateWithFindHelper(filePath, filter, runInShell, toFilePath, infoArgs); -} - -static void findUsingLs(const QString ¤t, - const FileFilter &filter, - const std::function &runInShell, - QStringList *found) -{ - const RunResult result = runInShell({"ls", {"-1", "-p", "--", current}}); - const QStringList entries = QString::fromUtf8(result.stdOut).split('\n', Qt::SkipEmptyParts); - for (QString entry : entries) { - const QChar last = entry.back(); - if (last == '/') { - entry.chop(1); - if (filter.iteratorFlags.testFlag(QDirIterator::Subdirectories)) - findUsingLs(current + '/' + entry, filter, runInShell, found); - } - found->append(entry); - } -} - -void FileUtils::iterateUnixDirectory(const FilePath &filePath, - const FileFilter &filter, - bool *useFind, - const std::function &runInShell, - const FilePath::IterateDirCallback &callBack) -{ - // We try to use 'find' first, because that can filter better directly. - // Unfortunately, it's not installed on all devices by default. - if (useFind && *useFind) { - if (iterateWithFind(filePath, filter, runInShell, callBack)) - return; - *useFind = false; // remember the failure for the next time and use the 'ls' fallback below. - } - - // if we do not have find - use ls as fallback - QStringList entries; - findUsingLs(filePath.path(), filter, runInShell, &entries); - iterateLsOutput(filePath, entries, filter, callBack); -} - /*! Copies the directory specified by \a srcFilePath recursively to \a tgtFilePath. \a tgtFilePath will contain the target directory, which will be created. Example usage: diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index 9949a82d858..76ad91efbc8 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -86,13 +86,6 @@ public: static FilePaths toFilePathList(const QStringList &paths); - static void iterateUnixDirectory( - const FilePath &base, - const FileFilter &filter, - bool *useFind, - const std::function &runInShell, - const FilePath::IterateDirCallback &callBack); - static qint64 bytesAvailableFromDFOutput(const QByteArray &dfOutput); static FilePathInfo filePathInfoFromTriple(const QString &infos); diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index b5da80bbc5f..4cc71591c35 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -83,6 +83,8 @@ Project { "detailsbutton.h", "detailswidget.cpp", "detailswidget.h", + "devicefileaccess.cpp", + "devicefileaccess.h", "deviceshell.cpp", "deviceshell.h", "differ.cpp", diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index bcab94bcb8b..be7124c995e 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -113,6 +114,25 @@ private: FilePath m_devicePath; }; +class DockerDeviceFileAccess : public UnixDeviceFileAccess +{ +public: + DockerDeviceFileAccess(DockerDevicePrivate *dev) + : m_dev(dev) + {} + + RunResult runInShell(const QString &executable, + const QStringList &arguments, + const QByteArray &stdInData) const override; + + std::optional fileContents( + const FilePath &filePath, + qint64 limit, + qint64 offset) const override; + + DockerDevicePrivate *m_dev = nullptr; +}; + class DockerDevicePrivate : public QObject { public: @@ -129,8 +149,6 @@ public: return runInShell(cmd, stdInData).exitCode == 0; } - std::optional fileContents(const FilePath &filePath, qint64 limit, qint64 offset); - void updateContainerAccess(); void changeMounts(QStringList newMounts); bool ensureReachable(const FilePath &other); @@ -177,8 +195,8 @@ public: QString m_container; Environment m_cachedEnviroment; - bool m_useFind = true; // prefer find over ls and hacks, but be able to use ls as fallback bool m_isShutdown = false; + DockerDeviceFileAccess m_fileAccess{this}; }; class DockerProcessImpl : public Utils::ProcessInterface @@ -337,9 +355,28 @@ Tasks DockerDevicePrivate::validateMounts() const return result; } +RunResult DockerDeviceFileAccess::runInShell( + const QString &executable, + const QStringList &arguments, + const QByteArray &stdInData) const +{ + QTC_ASSERT(m_dev, return {}); + return m_dev->runInShell({FilePath::fromString(executable), arguments}, stdInData); +} + +std::optional DockerDeviceFileAccess::fileContents( + const FilePath &filePath, + qint64 limit, + qint64 offset) const +{ + m_dev->updateContainerAccess(); + return UnixDeviceFileAccess::fileContents(filePath, limit, offset); +} + DockerDevice::DockerDevice(DockerSettings *settings, const DockerDeviceData &data) : d(new DockerDevicePrivate(this, settings, data)) { + setFileAccess(&d->m_fileAccess); setDisplayType(Tr::tr("Docker")); setOsType(OsTypeOtherUnix); setDefaultDisplayName(Tr::tr("Docker Image")); @@ -778,165 +815,6 @@ bool DockerDevice::handlesFile(const FilePath &filePath) const return false; } -bool DockerDevice::isExecutableFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-x", path}}); -} - -bool DockerDevice::isReadableFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-r", path, "-a", "-f", path}}); -} - -bool DockerDevice::isWritableFile(const Utils::FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-w", path, "-a", "-f", path}}); -} - -bool DockerDevice::isReadableDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-r", path, "-a", "-d", path}}); -} - -bool DockerDevice::isWritableDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-w", path, "-a", "-d", path}}); -} - -bool DockerDevice::isFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-f", path}}); -} - -bool DockerDevice::isDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-d", path}}); -} - -bool DockerDevice::createDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"mkdir", {"-p", path}}); -} - -bool DockerDevice::exists(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-e", path}}); -} - -bool DockerDevice::ensureExistingFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"touch", {path}}); -} - -bool DockerDevice::removeFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return d->runInShellSuccess({"rm", {filePath.path()}}); -} - -bool DockerDevice::removeRecursively(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - QTC_ASSERT(filePath.path().startsWith('/'), return false); - - const QString path = filePath.cleanPath().path(); - // We are expecting this only to be called in a context of build directories or similar. - // Chicken out in some cases that _might_ be user code errors. - QTC_ASSERT(path.startsWith('/'), return false); - const int levelsNeeded = path.startsWith("/home/") ? 4 : 3; - QTC_ASSERT(path.count('/') >= levelsNeeded, return false); - - return d->runInShellSuccess({"rm", {"-rf", "--", path}}); -} - -bool DockerDevice::copyFile(const FilePath &filePath, const FilePath &target) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - QTC_ASSERT(handlesFile(target), return false); - return d->runInShellSuccess({"cp", {filePath.path(), target.path()}}); -} - -bool DockerDevice::renameFile(const FilePath &filePath, const FilePath &target) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - QTC_ASSERT(handlesFile(target), return false); - return d->runInShellSuccess({"mv", {filePath.path(), target.path()}}); -} - -QDateTime DockerDevice::lastModified(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - const RunResult result = d->runInShell({"stat", {"-L", "-c", "%Y", filePath.path()}}); - qint64 secs = result.stdOut.toLongLong(); - const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC); - return dt; -} - -FilePath DockerDevice::symLinkTarget(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - const RunResult result = d->runInShell({"readlink", {"-n", "-e", filePath.path()}}); - const QString out = QString::fromUtf8(result.stdOut); - return out.isEmpty() ? FilePath() : filePath.withNewPath(out); -} - -qint64 DockerDevice::fileSize(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return -1); - const RunResult result = d->runInShell({"stat", {"-L", "-c", "%s", filePath.path()}}); - return result.stdOut.toLongLong(); -} - -QFileDevice::Permissions DockerDevice::permissions(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - - const RunResult result = d->runInShell({"stat", {"-L", "-c", "%a", filePath.path()}}); - const uint bits = result.stdOut.toUInt(nullptr, 8); - QFileDevice::Permissions perm = {}; -#define BIT(n, p) if (bits & (1<ensureReachable(other.parentDir()); } -void DockerDevice::iterateDirectory(const FilePath &filePath, - const FilePath::IterateDirCallback &callBack, - const FileFilter &filter) const -{ - QTC_ASSERT(handlesFile(filePath), return); - auto runInShell = [this](const CommandLine &cmd) { return d->runInShell(cmd); }; - FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack); -} - -std::optional DockerDevice::fileContents(const FilePath &filePath, - qint64 limit, - qint64 offset) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - return d->fileContents(filePath, limit, offset); -} - -bool DockerDevice::writeFileContents(const FilePath &filePath, - const QByteArray &data, - qint64 offset) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - CommandLine cmd({"dd", {"of=" + filePath.path()}}); - if (offset != 0) { - cmd.addArg("bs=1"); - cmd.addArg(QString("seek=%1").arg(offset)); - } - return d->runInShellSuccess(cmd, data); -} - -FilePathInfo DockerDevice::filePathInfo(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - const RunResult stat = d->runInShell({"stat", {"-L", "-c", "%f %Y %s", filePath.path()}}); - return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut)); -} - Environment DockerDevice::systemEnvironment() const { return d->environment(); @@ -995,28 +836,6 @@ void DockerDevice::aboutToBeRemoved() const detector.undoAutoDetect(id().toString()); } -std::optional DockerDevicePrivate::fileContents(const FilePath &filePath, - qint64 limit, - qint64 offset) -{ - updateContainerAccess(); - - QStringList args = {"if=" + filePath.path(), "status=none"}; - if (limit > 0 || offset > 0) { - const qint64 gcd = std::gcd(limit, offset); - args += {QString("bs=%1").arg(gcd), - QString("count=%1").arg(limit / gcd), - QString("seek=%1").arg(offset / gcd)}; - } - - const RunResult r = m_shell->runInShell({"dd", args}); - - if (r.exitCode != 0) - return {}; - - return r.stdOut; -} - void DockerDevicePrivate::fetchSystemEnviroment() { updateContainerAccess(); diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h index 1382dc7d7b6..625b45b46ff 100644 --- a/src/plugins/docker/dockerdevice.h +++ b/src/plugins/docker/dockerdevice.h @@ -84,37 +84,6 @@ public: Utils::FilePath rootPath() const override; bool handlesFile(const Utils::FilePath &filePath) const override; - bool isExecutableFile(const Utils::FilePath &filePath) const override; - bool isReadableFile(const Utils::FilePath &filePath) const override; - bool isWritableFile(const Utils::FilePath &filePath) const override; - bool isReadableDirectory(const Utils::FilePath &filePath) const override; - bool isWritableDirectory(const Utils::FilePath &filePath) const override; - bool isFile(const Utils::FilePath &filePath) const override; - bool isDirectory(const Utils::FilePath &filePath) const override; - bool createDirectory(const Utils::FilePath &filePath) const override; - bool exists(const Utils::FilePath &filePath) const override; - bool ensureExistingFile(const Utils::FilePath &filePath) const override; - bool removeFile(const Utils::FilePath &filePath) const override; - bool removeRecursively(const Utils::FilePath &filePath) const override; - bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; - bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; - Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override; - void iterateDirectory( - const Utils::FilePath &filePath, - const Utils::FilePath::IterateDirCallback &callBack, - const Utils::FileFilter &filter) const override; - std::optional fileContents(const Utils::FilePath &filePath, - qint64 limit, - qint64 offset) const override; - bool writeFileContents(const Utils::FilePath &filePath, - const QByteArray &data, - qint64 offset) const override; - Utils::FilePathInfo filePathInfo(const Utils::FilePath &filePath) const override; - QDateTime lastModified(const Utils::FilePath &filePath) const override; - qint64 fileSize(const Utils::FilePath &filePath) const override; - QFileDevice::Permissions permissions(const Utils::FilePath &filePath) const override; - bool setPermissions(const Utils::FilePath &filePath, - QFileDevice::Permissions permissions) const override; bool ensureReachable(const Utils::FilePath &other) const override; Utils::Environment systemEnvironment() const override; diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp index ee5dae12e09..e65e9892a18 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,8 @@ namespace ProjectExplorer { DesktopDevice::DesktopDevice() { + setFileAccess(DesktopDeviceFileAccess::instance()); + setupId(IDevice::AutoDetected, DESKTOP_DEVICE_ID); setType(DESKTOP_DEVICE_TYPE); setDefaultDisplayName(tr("Local PC")); @@ -136,33 +139,6 @@ bool DesktopDevice::handlesFile(const FilePath &filePath) const return !filePath.needsDevice(); } -void DesktopDevice::iterateDirectory( - const FilePath &filePath, - const FilePath::IterateDirCallback &callBack, - const FileFilter &filter) const -{ - QTC_CHECK(!filePath.needsDevice()); - filePath.iterateDirectory(callBack, filter); -} - -qint64 DesktopDevice::fileSize(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return -1); - return filePath.fileSize(); -} - -QFile::Permissions DesktopDevice::permissions(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - return filePath.permissions(); -} - -bool DesktopDevice::setPermissions(const FilePath &filePath, QFile::Permissions permissions) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - return filePath.setPermissions(permissions); -} - FilePath DesktopDevice::mapToGlobalPath(const Utils::FilePath &pathOnDevice) const { QTC_CHECK(!pathOnDevice.needsDevice()); @@ -174,117 +150,4 @@ Environment DesktopDevice::systemEnvironment() const return Environment::systemEnvironment(); } -bool DesktopDevice::isExecutableFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.isExecutableFile(); -} - -bool DesktopDevice::isReadableFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.isReadableFile(); -} - -bool DesktopDevice::isWritableFile(const Utils::FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.isWritableFile(); -} - -bool DesktopDevice::isReadableDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.isReadableDir(); -} - -bool DesktopDevice::isWritableDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.isWritableDir(); -} - -bool DesktopDevice::isFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.isFile(); -} - -bool DesktopDevice::isDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.isDir(); -} - -bool DesktopDevice::createDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.createDir(); -} - -bool DesktopDevice::exists(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.exists(); -} - -bool DesktopDevice::ensureExistingFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.ensureExistingFile(); -} - -bool DesktopDevice::removeFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.removeFile(); -} - -bool DesktopDevice::removeRecursively(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.removeRecursively(); -} - -bool DesktopDevice::copyFile(const FilePath &filePath, const FilePath &target) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return filePath.copyFile(target); -} - -bool DesktopDevice::renameFile(const FilePath &filePath, const FilePath &target) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - QTC_ASSERT(handlesFile(target), return false); - return filePath.renameFile(target); -} - -QDateTime DesktopDevice::lastModified(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - return filePath.lastModified(); -} - -FilePath DesktopDevice::symLinkTarget(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - return filePath.symLinkTarget(); -} - -std::optional DesktopDevice::fileContents(const FilePath &filePath, - qint64 limit, - qint64 offset) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - return filePath.fileContents(limit, offset); -} - -bool DesktopDevice::writeFileContents(const FilePath &filePath, - const QByteArray &data, - qint64 offset) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - return filePath.writeFileContents(data, offset); -} - } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.h b/src/plugins/projectexplorer/devicesupport/desktopdevice.h index d0d89f7458c..7144eba8eea 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.h +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.h @@ -34,35 +34,6 @@ public: bool handlesFile(const Utils::FilePath &filePath) const override; Utils::Environment systemEnvironment() const override; - bool isExecutableFile(const Utils::FilePath &filePath) const override; - bool isReadableFile(const Utils::FilePath &filePath) const override; - bool isWritableFile(const Utils::FilePath &filePath) const override; - bool isReadableDirectory(const Utils::FilePath &filePath) const override; - bool isWritableDirectory(const Utils::FilePath &filePath) const override; - bool isFile(const Utils::FilePath &filePath) const override; - bool isDirectory(const Utils::FilePath &filePath) const override; - bool ensureExistingFile(const Utils::FilePath &filePath) const override; - bool createDirectory(const Utils::FilePath &filePath) const override; - bool exists(const Utils::FilePath &filePath) const override; - bool removeFile(const Utils::FilePath &filePath) const override; - bool removeRecursively(const Utils::FilePath &filePath) const override; - bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; - bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; - QDateTime lastModified(const Utils::FilePath &filePath) const override; - Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override; - void iterateDirectory( - const Utils::FilePath &filePath, - const Utils::FilePath::IterateDirCallback &callBack, - const Utils::FileFilter &filter) const override; - std::optional fileContents(const Utils::FilePath &filePath, - qint64 limit, - qint64 offset) const override; - bool writeFileContents(const Utils::FilePath &filePath, - const QByteArray &data, - qint64 offset) const override; - qint64 fileSize(const Utils::FilePath &filePath) const override; - QFile::Permissions permissions(const Utils::FilePath &filePath) const override; - bool setPermissions(const Utils::FilePath &filePath, QFile::Permissions) const override; Utils::FilePath mapToGlobalPath(const Utils::FilePath &pathOnDevice) const override; protected: diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index 8a98a11b292..2d7d060427b 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -9,7 +9,9 @@ #include #include + #include +#include #include #include #include @@ -405,30 +407,6 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_uniqueisExecutableFile(filePath); - }; - - deviceHooks.isReadableFile = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->isReadableFile(filePath); - }; - - deviceHooks.isReadableDir = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->isReadableDirectory(filePath); - }; - - deviceHooks.isWritableDir = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->isWritableDirectory(filePath); - }; - deviceHooks.isSameDevice = [](const FilePath &left, const FilePath &right) { auto leftDevice = DeviceManager::deviceForPath(left); auto rightDevice = DeviceManager::deviceForPath(right); @@ -436,150 +414,12 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique DeviceFileAccess * { + if (!filePath.needsDevice()) + return DesktopDeviceFileAccess::instance(); auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->isWritableFile(filePath); - }; - - deviceHooks.isFile = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->isFile(filePath); - }; - - deviceHooks.isDir = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->isDirectory(filePath); - }; - - deviceHooks.ensureWritableDir = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->ensureWritableDirectory(filePath); - }; - - deviceHooks.ensureExistingFile = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->ensureExistingFile(filePath); - }; - - deviceHooks.createDir = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->createDirectory(filePath); - }; - - deviceHooks.exists = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->exists(filePath); - }; - - deviceHooks.removeFile = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->removeFile(filePath); - }; - - deviceHooks.removeRecursively = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->removeRecursively(filePath); - }; - - deviceHooks.copyFile = [](const FilePath &filePath, const FilePath &target) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->copyFile(filePath, target); - }; - - deviceHooks.renameFile = [](const FilePath &filePath, const FilePath &target) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->renameFile(filePath, target); - }; - - deviceHooks.searchInPath = [](const FilePath &filePath, const FilePaths &dirs) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return FilePath{}); - return device->searchExecutable(filePath.path(), dirs); - }; - - deviceHooks.symLinkTarget = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return FilePath{}); - return device->symLinkTarget(filePath); - }; - - deviceHooks.mapToDevicePath = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return QString{}); - return device->mapToDevicePath(filePath); - }; - - deviceHooks.iterateDirectory = [](const FilePath &filePath, - const FilePath::IterateDirCallback &callBack, - const FileFilter &filter) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return ); - device->iterateDirectory(filePath, callBack, filter); - }; - - deviceHooks.fileContents = - [](const FilePath &filePath, qint64 maxSize, qint64 offset) -> std::optional { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return {}); - return device->fileContents(filePath, maxSize, offset); - }; - - deviceHooks.asyncFileContents = [](const Continuation> &cont, - const FilePath &filePath, - qint64 maxSize, - qint64 offset) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return); - device->asyncFileContents(cont, filePath, maxSize, offset); - }; - - deviceHooks.writeFileContents = [](const FilePath &filePath, - const QByteArray &data, - qint64 offset) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->writeFileContents(filePath, data, offset); - }; - - deviceHooks.filePathInfo = [](const FilePath &filePath) -> FilePathInfo { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return {}); - return device->filePathInfo(filePath); - }; - - deviceHooks.lastModified = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return QDateTime()); - return device->lastModified(filePath); - }; - - deviceHooks.permissions = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return QFile::Permissions()); - return device->permissions(filePath); - }; - - deviceHooks.setPermissions = [](const FilePath &filePath, QFile::Permissions permissions) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return false); - return device->setPermissions(filePath, permissions); - }; - - deviceHooks.osType = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return OsTypeOther); - return device->osType(); + QTC_ASSERT(device, return nullptr); + return device->fileAccess(); }; deviceHooks.environment = [](const FilePath &filePath) { @@ -588,18 +428,6 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_uniquesystemEnvironment(); }; - deviceHooks.fileSize = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return qint64(-1)); - return device->fileSize(filePath); - }; - - deviceHooks.bytesAvailable = [](const FilePath &filePath) { - auto device = DeviceManager::deviceForPath(filePath); - QTC_ASSERT(device, return qint64(-1)); - return device->bytesAvailable(filePath); - }; - deviceHooks.deviceDisplayName = [](const FilePath &filePath) { auto device = DeviceManager::deviceForPath(filePath); QTC_ASSERT(device, return QString()); diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index ed6dcc73824..110f3f93fd0 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -15,6 +15,7 @@ #include +#include #include #include #include @@ -130,6 +131,7 @@ public: IDevice::DeviceState deviceState = IDevice::DeviceStateUnknown; IDevice::MachineType machineType = IDevice::Hardware; OsType osType = OsTypeOther; + DeviceFileAccess *fileAccess = nullptr; int version = 0; // This is used by devices that have been added by the SDK. QReadWriteLock lock; // Currently used to protect sshParameters only @@ -153,6 +155,8 @@ IDevice::IDevice() : d(new Internal::IDevicePrivate) { } +IDevice::~IDevice() = default; + void IDevice::setOpenTerminal(const IDevice::OpenTerminal &openTerminal) { d->openTerminal = openTerminal; @@ -191,6 +195,11 @@ bool IDevice::isAnyUnixDevice() const return d->osType == OsTypeLinux || d->osType == OsTypeMac || d->osType == OsTypeOtherUnix; } +DeviceFileAccess *IDevice::fileAccess() const +{ + return d->fileAccess; +} + FilePath IDevice::mapToGlobalPath(const FilePath &pathOnDevice) const { if (pathOnDevice.needsDevice()) { @@ -219,113 +228,6 @@ bool IDevice::handlesFile(const FilePath &filePath) const return false; } -bool IDevice::isExecutableFile(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::isReadableFile(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::isWritableFile(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::isReadableDirectory(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::isWritableDirectory(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::isFile(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::isDirectory(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::ensureWritableDirectory(const FilePath &filePath) const -{ - if (isWritableDirectory(filePath)) - return true; - return createDirectory(filePath); -} - -bool IDevice::ensureExistingFile(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::createDirectory(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::exists(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::removeFile(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::removeRecursively(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - -bool IDevice::copyFile(const FilePath &filePath, const FilePath &target) const -{ - Q_UNUSED(filePath); - Q_UNUSED(target); - QTC_CHECK(false); - return false; -} - -bool IDevice::renameFile(const FilePath &filePath, const FilePath &target) const -{ - Q_UNUSED(filePath); - Q_UNUSED(target); - QTC_CHECK(false); - return false; -} - FilePath IDevice::searchExecutableInPath(const QString &fileName) const { FilePaths paths; @@ -341,108 +243,13 @@ FilePath IDevice::searchExecutable(const QString &fileName, const FilePaths &dir dir = mapToGlobalPath(dir); QTC_CHECK(handlesFile(dir)); const FilePath candidate = dir / fileName; - if (isExecutableFile(candidate)) + if (candidate.isExecutableFile()) return candidate; } return {}; } -FilePath IDevice::symLinkTarget(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return {}; -} - -void IDevice::iterateDirectory(const FilePath &filePath, - const FilePath::IterateDirCallback &callBack, - const FileFilter &filter) const -{ - Q_UNUSED(filePath); - Q_UNUSED(callBack); - Q_UNUSED(filter); - QTC_CHECK(false); -} - -std::optional IDevice::fileContents(const FilePath &filePath, - qint64 limit, - qint64 offset) const -{ - Q_UNUSED(filePath); - Q_UNUSED(limit); - Q_UNUSED(offset); - QTC_CHECK(false); - return {}; -} - -void IDevice::asyncFileContents(const Continuation> &cont, - const FilePath &filePath, - qint64 limit, - qint64 offset) const -{ - cont(fileContents(filePath, limit, offset)); -} - -bool IDevice::writeFileContents(const FilePath &filePath, const QByteArray &data, qint64 offset) const -{ - Q_UNUSED(filePath); - Q_UNUSED(data); - Q_UNUSED(offset); - QTC_CHECK(false); - return {}; -} - -FilePathInfo IDevice::filePathInfo(const Utils::FilePath &filePath) const -{ - bool exists = filePath.exists(); - if (!exists) - return {}; - - FilePathInfo result { - filePath.fileSize(), - {FilePathInfo::ExistsFlag}, - filePath.lastModified(), - }; - - if (filePath.isDir()) - result.fileFlags |= FilePathInfo::DirectoryType; - if (filePath.isFile()) - result.fileFlags |= FilePathInfo::FileType; - if (filePath.isRootPath()) - result.fileFlags |= FilePathInfo::RootFlag; - - return result; -} - -void IDevice::asyncWriteFileContents(const Continuation &cont, - const FilePath &filePath, - const QByteArray &data, - qint64 offset) const -{ - cont(writeFileContents(filePath, data, offset)); -} - -QDateTime IDevice::lastModified(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - return {}; -} - -QFileDevice::Permissions IDevice::permissions(const FilePath &filePath) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return {}; -} - -bool IDevice::setPermissions(const FilePath &filePath, QFile::Permissions) const -{ - Q_UNUSED(filePath); - QTC_CHECK(false); - return false; -} - ProcessInterface *IDevice::createProcessInterface() const { QTC_CHECK(false); @@ -463,22 +270,6 @@ Environment IDevice::systemEnvironment() const return Environment::systemEnvironment(); } -qint64 IDevice::fileSize(const FilePath &filePath) const -{ - Q_UNUSED(filePath) - QTC_CHECK(false); - return -1; -} - -qint64 IDevice::bytesAvailable(const Utils::FilePath &filePath) const -{ - Q_UNUSED(filePath) - QTC_CHECK(false); - return -1; -} - -IDevice::~IDevice() = default; - /*! Specifies a free-text name for the device to be displayed in GUI elements. */ @@ -513,6 +304,11 @@ void IDevice::setOsType(OsType osType) d->osType = osType; } +void IDevice::setFileAccess(DeviceFileAccess *fileAccess) +{ + d->fileAccess = fileAccess; +} + IDevice::DeviceInfo IDevice::deviceInformation() const { const QString key = QCoreApplication::translate("ProjectExplorer::IDevice", "Device"); diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h index ac46ff77f47..0f5649afd6c 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.h +++ b/src/plugins/projectexplorer/devicesupport/idevice.h @@ -27,6 +27,7 @@ QT_END_NAMESPACE namespace Utils { class CommandLine; +class DeviceFileAccess; class Environment; class Icon; class PortList; @@ -214,62 +215,23 @@ public: bool isMacDevice() const { return osType() == Utils::OsTypeMac; } bool isAnyUnixDevice() const; + Utils::DeviceFileAccess *fileAccess() const; + virtual bool handlesFile(const Utils::FilePath &filePath) const; + virtual Utils::FilePath mapToGlobalPath(const Utils::FilePath &pathOnDevice) const; virtual QString mapToDevicePath(const Utils::FilePath &globalPath) const; - virtual bool handlesFile(const Utils::FilePath &filePath) const; - virtual bool isExecutableFile(const Utils::FilePath &filePath) const; - virtual bool isReadableFile(const Utils::FilePath &filePath) const; - virtual bool isWritableFile(const Utils::FilePath &filePath) const; - virtual bool isReadableDirectory(const Utils::FilePath &filePath) const; - virtual bool isWritableDirectory(const Utils::FilePath &filePath) const; - virtual bool isFile(const Utils::FilePath &filePath) const; - virtual bool isDirectory(const Utils::FilePath &filePath) const; - virtual bool ensureWritableDirectory(const Utils::FilePath &filePath) const; - virtual bool ensureExistingFile(const Utils::FilePath &filePath) const; - virtual bool createDirectory(const Utils::FilePath &filePath) const; - virtual bool exists(const Utils::FilePath &filePath) const; - virtual bool removeFile(const Utils::FilePath &filePath) const; - virtual bool removeRecursively(const Utils::FilePath &filePath) const; - virtual bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const; - virtual bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const; virtual Utils::FilePath searchExecutableInPath(const QString &fileName) const; virtual Utils::FilePath searchExecutable(const QString &fileName, const Utils::FilePaths &dirs) const; - virtual Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const; - virtual void iterateDirectory( - const Utils::FilePath &filePath, - const Utils::FilePath::IterateDirCallback &callBack, - const Utils::FileFilter &filter) const; - virtual std::optional fileContents(const Utils::FilePath &filePath, - qint64 limit, - qint64 offset) const; - virtual bool writeFileContents(const Utils::FilePath &filePath, - const QByteArray &data, - qint64 offset) const; - virtual Utils::FilePathInfo filePathInfo(const Utils::FilePath &filePath) const; - virtual QDateTime lastModified(const Utils::FilePath &filePath) const; - virtual QFile::Permissions permissions(const Utils::FilePath &filePath) const; - virtual bool setPermissions(const Utils::FilePath &filePath, QFile::Permissions) const; virtual Utils::ProcessInterface *createProcessInterface() const; virtual FileTransferInterface *createFileTransferInterface( const FileTransferSetupData &setup) const; virtual Utils::Environment systemEnvironment() const; - virtual qint64 fileSize(const Utils::FilePath &filePath) const; - virtual qint64 bytesAvailable(const Utils::FilePath &filePath) const; virtual void aboutToBeRemoved() const {} - virtual void asyncFileContents(const Continuation> &cont, - const Utils::FilePath &filePath, - qint64 limit, - qint64 offset) const; - virtual void asyncWriteFileContents(const Continuation &cont, - const Utils::FilePath &filePath, - const QByteArray &data, - qint64 offset) const; - virtual bool ensureReachable(const Utils::FilePath &other) const; virtual bool prepareForBuild(const Target *target); @@ -284,6 +246,7 @@ protected: void setOpenTerminal(const OpenTerminal &openTerminal); void setDisplayType(const QString &type); void setOsType(Utils::OsType osType); + void setFileAccess(Utils::DeviceFileAccess *fileAccess); private: IDevice(const IDevice &) = delete; diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index dae5d277fd2..884412f5b5f 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -1118,8 +1118,7 @@ static FilePaths findCompilerCandidates(const ToolchainDetector &detector, return true; }; const FilePath globalDir = device->mapToGlobalPath(deviceDir); - device->iterateDirectory(globalDir, callBack, - {nameFilters, QDir::Files | QDir::Executable}); + globalDir.iterateDirectory(callBack, {nameFilters, QDir::Files | QDir::Executable}); } } else { // The normal, local host case. diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index f9018e04e78..b5b9205377d 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -349,6 +350,21 @@ private: // LinuxDevicePrivate class ShellThreadHandler; +class LinuxDevicePrivate; + +class LinuxDeviceFileAccess : public UnixDeviceFileAccess +{ +public: + LinuxDeviceFileAccess(LinuxDevicePrivate *dev) + : m_dev(dev) + {} + + RunResult runInShell(const QString &executable, + const QStringList &arguments, + const QByteArray &stdInData) const override; + + LinuxDevicePrivate *m_dev; +}; class LinuxDevicePrivate { @@ -358,9 +374,6 @@ public: bool setupShell(); RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {}); - bool runInShellSuccess(const CommandLine &cmd, const QByteArray &stdInData = {}) { - return runInShell(cmd, stdInData).exitCode == 0; - } void attachToSharedConnection(SshConnectionHandle *connectionHandle, const SshParameters &sshParameters); @@ -370,9 +383,16 @@ public: ShellThreadHandler *m_handler = nullptr; mutable QMutex m_shellMutex; QList m_terminals; - bool m_useFind = true; + LinuxDeviceFileAccess m_fileAccess{this}; }; +RunResult LinuxDeviceFileAccess::runInShell(const QString &executable, + const QStringList &arguments, + const QByteArray &stdInData) const +{ + return m_dev->runInShell({FilePath::fromString(executable), arguments}, stdInData); +} + // SshProcessImpl class SshProcessInterfacePrivate : public QObject @@ -465,7 +485,7 @@ qint64 SshProcessInterface::processId() const bool SshProcessInterface::runInShell(const CommandLine &command, const QByteArray &data) { - return d->m_devicePrivate->runInShellSuccess(command, data); + return d->m_devicePrivate->runInShell(command, data).exitCode == 0; } void SshProcessInterface::start() @@ -904,6 +924,7 @@ private: LinuxDevice::LinuxDevice() : d(new LinuxDevicePrivate(this)) { + setFileAccess(&d->m_fileAccess); setDisplayType(Tr::tr("Remote Linux")); setDefaultDisplayName(Tr::tr("Remote Linux Device")); setOsType(OsTypeLinux); @@ -1121,223 +1142,6 @@ void LinuxDevicePrivate::attachToSharedConnection(SshConnectionHandle *connectio emit connectionHandle->connected(socketFilePath); } -bool LinuxDevice::isExecutableFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-x", path}}); -} - -bool LinuxDevice::isReadableFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-r", path, "-a", "-f", path}}); -} - -bool LinuxDevice::isWritableFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-w", path, "-a", "-f", path}}); -} - -bool LinuxDevice::isReadableDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-r", path, "-a", "-d", path}}); -} - -bool LinuxDevice::isWritableDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-w", path, "-a", "-d", path}}); -} - -bool LinuxDevice::isFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-f", path}}); -} - -bool LinuxDevice::isDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-d", path}}); -} - -bool LinuxDevice::createDirectory(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"mkdir", {"-p", path}}); -} - -bool LinuxDevice::exists(const FilePath &filePath) const -{ - DEBUG("filepath " << filePath.path()); - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"test", {"-e", path}}); -} - -bool LinuxDevice::ensureExistingFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - const QString path = filePath.path(); - return d->runInShellSuccess({"touch", {path}}); -} - -bool LinuxDevice::removeFile(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - return d->runInShellSuccess({"rm", {filePath.path()}}); -} - -bool LinuxDevice::removeRecursively(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - QTC_ASSERT(filePath.path().startsWith('/'), return false); - - const QString path = filePath.cleanPath().path(); - // We are expecting this only to be called in a context of build directories or similar. - // Chicken out in some cases that _might_ be user code errors. - QTC_ASSERT(path.startsWith('/'), return false); - const int levelsNeeded = path.startsWith("/home/") ? 3 : 2; - QTC_ASSERT(path.count('/') >= levelsNeeded, return false); - - return d->runInShellSuccess({"rm", {"-rf", "--", path}}); -} - -bool LinuxDevice::copyFile(const FilePath &filePath, const FilePath &target) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - QTC_ASSERT(handlesFile(target), return false); - return d->runInShellSuccess({"cp", {filePath.path(), target.path()}}); -} - -bool LinuxDevice::renameFile(const FilePath &filePath, const FilePath &target) const -{ - QTC_ASSERT(handlesFile(filePath), return false); - QTC_ASSERT(handlesFile(target), return false); - return d->runInShellSuccess({"mv", {filePath.path(), target.path()}}); -} - -QDateTime LinuxDevice::lastModified(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - const RunResult result = d->runInShell({"stat", {"-L", "-c", "%Y", filePath.path()}}); - const qint64 secs = result.stdOut.toLongLong(); - const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC); - return dt; -} - -FilePath LinuxDevice::symLinkTarget(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - const RunResult result = d->runInShell({"readlink", {"-n", "-e", filePath.path()}}); - return result.stdOut.isEmpty() ? FilePath() - : filePath.withNewPath(QString::fromUtf8(result.stdOut)); -} - -qint64 LinuxDevice::fileSize(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return -1); - const RunResult result = d->runInShell({"stat", {"-L", "-c", "%s", filePath.path()}}); - return result.stdOut.toLongLong(); -} - -qint64 LinuxDevice::bytesAvailable(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return -1); - CommandLine cmd("df", {"-k"}); - cmd.addArg(filePath.path()); - const RunResult result = d->runInShell(cmd); - return FileUtils::bytesAvailableFromDFOutput(result.stdOut); -} - -QFileDevice::Permissions LinuxDevice::permissions(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - const RunResult result = d->runInShell({"stat", {"-L", "-c", "%a", filePath.path()}}); - const uint bits = result.stdOut.toUInt(nullptr, 8); - QFileDevice::Permissions perm = {}; -#define BIT(n, p) if (bits & (1<runInShellSuccess({"chmod", {QString::number(flags, 16), filePath.path()}}); -} - -void LinuxDevice::iterateDirectory(const FilePath &filePath, - const FilePath::IterateDirCallback &callBack, - const FileFilter &filter) const -{ - QTC_ASSERT(handlesFile(filePath), return); - auto runInShell = [this](const CommandLine &cmd) { return d->runInShell(cmd); }; - FileUtils::iterateUnixDirectory(filePath, filter, &d->m_useFind, runInShell, callBack); -} - -FilePathInfo LinuxDevice::filePathInfo(const FilePath &filePath) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - const RunResult stat = d->runInShell({"stat", {"-L", "-c", "%f %Y %s", filePath.path()}}); - return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut)); -} - -std::optional LinuxDevice::fileContents(const FilePath &filePath, - qint64 limit, - qint64 offset) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - QString args = "if=" + filePath.path() + " status=none"; - if (limit > 0 || offset > 0) { - const qint64 gcd = std::gcd(limit, offset); - args += QString(" bs=%1 count=%2 seek=%3").arg(gcd).arg(limit / gcd).arg(offset / gcd); - } - CommandLine cmd(FilePath::fromString("dd"), args, CommandLine::Raw); - - const RunResult result = d->runInShell(cmd); - if (result.exitCode != 0) { - DEBUG("fileContents failed"); - return {}; - } - - DEBUG(result.stdOut << QByteArray::fromHex(result.stdOut)); - return result.stdOut; -} - -bool LinuxDevice::writeFileContents(const FilePath &filePath, - const QByteArray &data, - qint64 offset) const -{ - QTC_ASSERT(handlesFile(filePath), return {}); - CommandLine cmd({"dd", {"of=" + filePath.path()}}); - if (offset != 0) { - cmd.addArg("bs=1"); - cmd.addArg(QString("seek=%1").arg(offset)); - } - return d->runInShellSuccess(cmd, data); -} - static FilePaths dirsToCreate(const FilesToTransfer &files) { FilePaths dirs; diff --git a/src/plugins/remotelinux/linuxdevice.h b/src/plugins/remotelinux/linuxdevice.h index b1e992c024a..51517ed8622 100644 --- a/src/plugins/remotelinux/linuxdevice.h +++ b/src/plugins/remotelinux/linuxdevice.h @@ -37,40 +37,11 @@ public: Utils::FilePath rootPath() const override; bool handlesFile(const Utils::FilePath &filePath) const override; - bool isExecutableFile(const Utils::FilePath &filePath) const override; - bool isReadableFile(const Utils::FilePath &filePath) const override; - bool isWritableFile(const Utils::FilePath &filePath) const override; - bool isReadableDirectory(const Utils::FilePath &filePath) const override; - bool isWritableDirectory(const Utils::FilePath &filePath) const override; - bool isFile(const Utils::FilePath &filePath) const override; - bool isDirectory(const Utils::FilePath &filePath) const override; - bool createDirectory(const Utils::FilePath &filePath) const override; - bool exists(const Utils::FilePath &filePath) const override; - bool ensureExistingFile(const Utils::FilePath &filePath) const override; - bool removeFile(const Utils::FilePath &filePath) const override; - bool removeRecursively(const Utils::FilePath &filePath) const override; - bool copyFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; - bool renameFile(const Utils::FilePath &filePath, const Utils::FilePath &target) const override; - Utils::FilePath symLinkTarget(const Utils::FilePath &filePath) const override; - void iterateDirectory(const Utils::FilePath &filePath, - const Utils::FilePath::IterateDirCallback &callBack, - const Utils::FileFilter &filter) const override; - std::optional fileContents(const Utils::FilePath &filePath, - qint64 limit, - qint64 offset) const override; - bool writeFileContents(const Utils::FilePath &filePath, - const QByteArray &data, - qint64 offset) const override; - Utils::FilePathInfo filePathInfo(const Utils::FilePath &filePath) const override; - QDateTime lastModified(const Utils::FilePath &filePath) const override; + Utils::ProcessInterface *createProcessInterface() const override; ProjectExplorer::FileTransferInterface *createFileTransferInterface( const ProjectExplorer::FileTransferSetupData &setup) const override; Utils::Environment systemEnvironment() const override; - qint64 fileSize(const Utils::FilePath &filePath) const override; - qint64 bytesAvailable(const Utils::FilePath &filePath) const override; - QFileDevice::Permissions permissions(const Utils::FilePath &filePath) const override; - bool setPermissions(const Utils::FilePath &filePath, QFileDevice::Permissions permissions) const override; protected: LinuxDevice(); diff --git a/src/tools/sdktool/CMakeLists.txt b/src/tools/sdktool/CMakeLists.txt index 050940aca96..ffd578dbcac 100644 --- a/src/tools/sdktool/CMakeLists.txt +++ b/src/tools/sdktool/CMakeLists.txt @@ -69,6 +69,7 @@ extend_qtc_library(sdktoolLib 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 diff --git a/src/tools/sdktool/sdktoollib.qbs b/src/tools/sdktool/sdktoollib.qbs index 4352c1f6c06..3c27d823023 100644 --- a/src/tools/sdktool/sdktoollib.qbs +++ b/src/tools/sdktool/sdktoollib.qbs @@ -92,6 +92,7 @@ QtcLibrary { prefix: libsDir + "/utils/" files: [ "commandline.cpp", "commandline.h", + "devicefileaccess.cpp", "devicefileaccess.h", "environment.cpp", "environment.h", "filepath.cpp", "filepath.h", "fileutils.cpp", "fileutils.h", diff --git a/tests/auto/utils/fsengine/tst_fsengine.cpp b/tests/auto/utils/fsengine/tst_fsengine.cpp index 997fe26fd97..e9a21353f99 100644 --- a/tests/auto/utils/fsengine/tst_fsengine.cpp +++ b/tests/auto/utils/fsengine/tst_fsengine.cpp @@ -2,8 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #include -#include #include +#include #include #include @@ -58,119 +58,6 @@ void tst_fsengine::initTestCase() if (HostOsInfo::isWindowsHost()) QSKIP("The fsengine tests are not supported on Windows."); - DeviceFileHooks &deviceHooks = DeviceFileHooks::instance(); - - deviceHooks.fileContents = - [](const FilePath &path, qint64 maxSize, qint64 offset) -> std::optional { - return FilePath::fromString(path.path()).fileContents(maxSize, offset); - }; - - deviceHooks.isExecutableFile = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).isExecutableFile(); - }; - deviceHooks.isReadableFile = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).isReadableFile(); - }; - deviceHooks.isReadableDir = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).isReadableDir(); - }; - deviceHooks.isWritableDir = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).isWritableDir(); - }; - deviceHooks.isWritableFile = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).isWritableFile(); - }; - deviceHooks.isFile = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).isFile(); - }; - deviceHooks.isDir = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).isDir(); - }; - deviceHooks.ensureWritableDir = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).ensureWritableDir(); - }; - deviceHooks.ensureExistingFile = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).ensureExistingFile(); - }; - deviceHooks.createDir = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).createDir(); - }; - deviceHooks.exists = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).exists(); - }; - deviceHooks.removeFile = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).removeFile(); - }; - deviceHooks.removeRecursively = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).removeRecursively(); - }; - deviceHooks.copyFile = [](const FilePath &filePath, const FilePath &target) { - return FilePath::fromString(filePath.path()).copyFile(target); - }; - deviceHooks.renameFile = [](const FilePath &filePath, const FilePath &target) { - return FilePath::fromString(filePath.path()).renameFile(target); - }; - deviceHooks.searchInPath = [](const FilePath &filePath, const FilePaths &dirs) { - return FilePath::fromString(filePath.path()).searchInPath(dirs); - }; - deviceHooks.symLinkTarget = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).symLinkTarget(); - }; - deviceHooks.iterateDirectory = [](const FilePath &filePath, - const FilePath::IterateDirCallback &callBack, - const FileFilter &filter) { - const FilePath fp = FilePath::fromString(filePath.path()); - if (callBack.index() == 0) { - fp.iterateDirectory( - [&filePath, cb = std::get<0>(callBack)](const FilePath &path) { - return cb(path.onDevice(filePath)); - }, - filter); - } else { - fp.iterateDirectory( - [&filePath, cb = std::get<1>(callBack)](const FilePath &path, const FilePathInfo &fpi) { - return cb(path.onDevice(filePath), fpi); - }, - filter); - } - }; - deviceHooks.asyncFileContents = [](const Continuation &> &cont, - const FilePath &filePath, - qint64 maxSize, - qint64 offset) { - return FilePath::fromString(filePath.path()).asyncFileContents(cont, maxSize, offset); - }; - deviceHooks.writeFileContents = [](const FilePath &filePath, - const QByteArray &data, - qint64 offset) { - return FilePath::fromString(filePath.path()).writeFileContents(data, offset); - }; - deviceHooks.lastModified = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).lastModified(); - }; - deviceHooks.permissions = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).permissions(); - }; - deviceHooks.setPermissions = [](const FilePath &filePath, QFile::Permissions permissions) { - return FilePath::fromString(filePath.path()).setPermissions(permissions); - }; - deviceHooks.osType = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).osType(); - }; - // deviceHooks.environment = [](const FilePath &filePath) -> Environment {return {};}; - deviceHooks.fileSize = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).fileSize(); - }; - deviceHooks.bytesAvailable = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).bytesAvailable(); - }; - deviceHooks.filePathInfo = [](const FilePath &filePath) { - return FilePath::fromString(filePath.path()).filePathInfo(); - }; - - - deviceHooks.mapToDevicePath = [](const FilePath &filePath) { return filePath.path(); }; - FSEngine::addDevice(FilePath::fromString("device://test")); tempFolder = QDir::tempPath(); From 767644967ca7d342e2d6b7cdd8b3ad4998d28f0a Mon Sep 17 00:00:00 2001 From: David Schulz Date: Wed, 12 Oct 2022 10:42:22 +0200 Subject: [PATCH 035/143] Debugger: remove can interrupt check We are now able to interrupt via Ctrl+C so we do not necessarily need a local inferior with a pid. Change-Id: I31997c5d2885b748699756908ef46686798ccafa Reviewed-by: Christian Stenger --- src/plugins/debugger/cdb/cdbengine.cpp | 8 -------- src/plugins/debugger/cdb/cdbengine.h | 1 - 2 files changed, 9 deletions(-) diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index b0da3a13637..7740d0f0208 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -611,9 +611,6 @@ void CdbEngine::shutdownInferior() if (commandsPending()) { showMessage("Cannot shut down inferior due to pending commands.", LogWarning); STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFinished") - } else if (!canInterruptInferior()) { - showMessage("Cannot interrupt the inferior.", LogWarning); - STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorShutdownFinished") } else { interruptInferior(); // Calls us again return; @@ -758,11 +755,6 @@ void CdbEngine::doContinueInferior() runCommand({"g", NoFlags}); } -bool CdbEngine::canInterruptInferior() const -{ - return m_effectiveStartMode != AttachToRemoteServer && inferiorPid(); -} - void CdbEngine::interruptInferior() { if (debug) diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index 4f3786bc631..27a3cd3d410 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -129,7 +129,6 @@ private: void doContinueInferior(); void parseOutputLine(QString line); bool isCdbProcessRunning() const { return m_process.state() != QProcess::NotRunning; } - bool canInterruptInferior() const; inline void postDisassemblerCommand(quint64 address, DisassemblerAgent *agent); void postDisassemblerCommand(quint64 address, quint64 endAddress, DisassemblerAgent *agent); From 17b1fa1618dc974afd19c26fbd0088e6d70c96fa Mon Sep 17 00:00:00 2001 From: David Schulz Date: Wed, 12 Oct 2022 11:59:26 +0200 Subject: [PATCH 036/143] Debugger: Fix interrupting with cdb It turns out sending ctrl+c events to cdb results in inconsistent behavior when trying to interrupt with the range from interrupting twice to not at all. Use the previous method of using signal operation for local debugging, and use the ctrl+c event only when cdb is attached to a remote server. Fixes: QTCREATORBUG-28279 Change-Id: Iccd2016685ba707b375aebfd88eccc253dde1d1d Reviewed-by: Christian Stenger --- src/plugins/debugger/cdb/cdbengine.cpp | 24 ++++++++++++++++++++++++ src/plugins/debugger/cdb/cdbengine.h | 3 +++ 2 files changed, 27 insertions(+) diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 7740d0f0208..05560351597 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -763,6 +763,18 @@ void CdbEngine::interruptInferior() doInterruptInferior(); } +void CdbEngine::handleDoInterruptInferior(const QString &errorMessage) +{ + if (errorMessage.isEmpty()) { + showMessage("Interrupted " + QString::number(inferiorPid())); + } else { + showMessage(errorMessage, LogError); + notifyInferiorStopFailed(); + } + m_signalOperation->disconnect(this); + m_signalOperation.clear(); +} + void CdbEngine::doInterruptInferior(const InterruptCallback &callback) { const bool requestInterrupt = m_stopMode == NoStopRequested; @@ -779,6 +791,18 @@ void CdbEngine::doInterruptInferior(const InterruptCallback &callback) if (!requestInterrupt) return; // we already requested a stop no need to interrupt twice showMessage(QString("Interrupting process %1...").arg(inferiorPid()), LogMisc); + + QTC_ASSERT(!m_signalOperation, notifyInferiorStopFailed(); return); + if (m_effectiveStartMode != AttachToRemoteServer && device()) { + m_signalOperation = device()->signalOperation(); + if (m_signalOperation) { + connect(m_signalOperation.data(), &DeviceProcessSignalOperation::finished, + this, &CdbEngine::handleDoInterruptInferior); + m_signalOperation->setDebuggerCommand(runParameters().debugger.command.executable()); + m_signalOperation->interruptProcess(inferiorPid()); + return; + } + } m_process.interrupt(); } diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index 27a3cd3d410..108cc64d160 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -85,6 +85,8 @@ private: void createFullBacktrace(); + void handleDoInterruptInferior(const QString &errorMessage); + typedef QPair SourcePathMapping; struct NormalizedSourceFileName // Struct for caching mapped/normalized source files. { @@ -175,6 +177,7 @@ private: //! Debugger accessible (expecting commands) bool m_accessible = false; StopMode m_stopMode = NoStopRequested; + ProjectExplorer::DeviceProcessSignalOperation::Ptr m_signalOperation; int m_nextCommandToken = 0; QHash m_commandForToken; QString m_currentBuiltinResponse; From 0f24475f484e8372c6f74169a6be21621a3d4862 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 11 Oct 2022 13:48:11 +0200 Subject: [PATCH 037/143] Squish: Adapt to changed ui MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Idbee4f2a4d1536cc1dc6a7822d4415032feb9a28 Reviewed-by: Robert Löhning --- tests/system/shared/clang.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/shared/clang.py b/tests/system/shared/clang.py index 3687e776da6..97de36b16ab 100644 --- a/tests/system/shared/clang.py +++ b/tests/system/shared/clang.py @@ -43,5 +43,5 @@ def getCodeModelString(useClang): def checkCodeModelSettings(useClang): __openCodeModelOptions__() test.log("Verifying whether 'Ignore pre-compiled headers' is unchecked by default.") - verifyChecked("{name='ignorePCHCheckBox' type='QCheckBox' visible='1'}", False) + verifyChecked("{text='Ignore precompiled headers' type='QCheckBox' visible='1'}", False) clickButton(waitForObject(":Options.OK_QPushButton")) From a22d62e57d900853d135859720920d2ddec4c4f2 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Thu, 13 Oct 2022 11:11:29 +0200 Subject: [PATCH 038/143] Utils: Add FilePath::isSameFile() FilePath::isSameFile() checks if two files are the same file. It first checks if its on the same device. If it is, it will try to read the fileId of the files and compare them. Change-Id: I83668955cacd4e5ed03d43a3fee2be29e9d0a6f0 Reviewed-by: hjk --- src/libs/utils/devicefileaccess.cpp | 95 ++++++++++++++++++++ src/libs/utils/devicefileaccess.h | 3 + src/libs/utils/environment.cpp | 8 +- src/libs/utils/filepath.cpp | 19 ++++ src/libs/utils/filepath.h | 1 + src/libs/utils/fileutils.cpp | 73 +-------------- src/libs/utils/fileutils.h | 1 - tests/auto/utils/fileutils/tst_fileutils.cpp | 45 ++++++++++ 8 files changed, 165 insertions(+), 80 deletions(-) diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp index 6b07edaf62b..63fc456c8ed 100644 --- a/src/libs/utils/devicefileaccess.cpp +++ b/src/libs/utils/devicefileaccess.cpp @@ -8,9 +8,20 @@ #include "hostosinfo.h" #include +#include #include #include +#ifdef Q_OS_WIN +#ifdef QTCREATOR_PCH_H +#define CALLBACK WINAPI +#endif +#include +#include +#else +#include +#endif + namespace Utils { // DeviceFileAccess @@ -220,6 +231,13 @@ qint64 DeviceFileAccess::bytesAvailable(const FilePath &filePath) const return -1; } +QByteArray DeviceFileAccess::fileId(const FilePath &filePath) const +{ + Q_UNUSED(filePath); + QTC_CHECK(false); + return {}; +} + void DeviceFileAccess::asyncFileContents( const FilePath &filePath, const Continuation> &cont, @@ -509,6 +527,74 @@ qint64 DesktopDeviceFileAccess::bytesAvailable(const FilePath &filePath) const return QStorageInfo(filePath.path()).bytesAvailable(); } +// Copied from qfilesystemengine_win.cpp +#ifdef Q_OS_WIN + +// File ID for Windows up to version 7. +static inline QByteArray fileIdWin7(HANDLE handle) +{ + BY_HANDLE_FILE_INFORMATION info; + if (GetFileInformationByHandle(handle, &info)) { + char buffer[sizeof "01234567:0123456701234567\0"]; + qsnprintf(buffer, sizeof(buffer), "%lx:%08lx%08lx", + info.dwVolumeSerialNumber, + info.nFileIndexHigh, + info.nFileIndexLow); + return QByteArray(buffer); + } + return QByteArray(); +} + +// File ID for Windows starting from version 8. +static QByteArray fileIdWin8(HANDLE handle) +{ + QByteArray result; + FILE_ID_INFO infoEx; + if (GetFileInformationByHandleEx(handle, + static_cast(18), // FileIdInfo in Windows 8 + &infoEx, sizeof(FILE_ID_INFO))) { + result = QByteArray::number(infoEx.VolumeSerialNumber, 16); + result += ':'; + // Note: MinGW-64's definition of FILE_ID_128 differs from the MSVC one. + result += QByteArray(reinterpret_cast(&infoEx.FileId), int(sizeof(infoEx.FileId))).toHex(); + } + return result; +} + +static QByteArray fileIdWin(HANDLE fHandle) +{ + return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8 ? + fileIdWin8(HANDLE(fHandle)) : fileIdWin7(HANDLE(fHandle)); +} +#endif + +QByteArray DesktopDeviceFileAccess::fileId(const FilePath &filePath) const +{ + QByteArray result; + +#ifdef Q_OS_WIN + const HANDLE handle = + CreateFile((wchar_t*)filePath.toUserOutput().utf16(), 0, + FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (handle != INVALID_HANDLE_VALUE) { + result = fileIdWin(handle); + CloseHandle(handle); + } +#else // Copied from qfilesystemengine_unix.cpp + if (Q_UNLIKELY(filePath.isEmpty())) + return result; + + QT_STATBUF statResult; + if (QT_STAT(filePath.toString().toLocal8Bit().constData(), &statResult)) + return result; + result = QByteArray::number(quint64(statResult.st_dev), 16); + result += ':'; + result += QByteArray::number(quint64(statResult.st_ino)); +#endif + return result; +} + OsType DesktopDeviceFileAccess::osType(const FilePath &filePath) const { Q_UNUSED(filePath); @@ -714,6 +800,15 @@ qint64 UnixDeviceFileAccess::bytesAvailable(const FilePath &filePath) const return FileUtils::bytesAvailableFromDFOutput(result.stdOut); } +QByteArray UnixDeviceFileAccess::fileId(const FilePath &filePath) const +{ + const RunResult result = runInShell("stat", {"-L", "-c", "%D:%i", filePath.path()}); + if (result.exitCode != 0) + return {}; + + return result.stdOut; +} + FilePathInfo UnixDeviceFileAccess::filePathInfo(const FilePath &filePath) const { const RunResult stat = runInShell("stat", {"-L", "-c", "%f %Y %s", filePath.path()}); diff --git a/src/libs/utils/devicefileaccess.h b/src/libs/utils/devicefileaccess.h index e1ef5db1ddb..f638d162606 100644 --- a/src/libs/utils/devicefileaccess.h +++ b/src/libs/utils/devicefileaccess.h @@ -44,6 +44,7 @@ protected: virtual bool setPermissions(const FilePath &filePath, QFile::Permissions) const; virtual qint64 fileSize(const FilePath &filePath) const; virtual qint64 bytesAvailable(const FilePath &filePath) const; + virtual QByteArray fileId(const FilePath &filePath) const; virtual void iterateDirectory( const FilePath &filePath, @@ -109,6 +110,7 @@ protected: bool setPermissions(const FilePath &filePath, QFile::Permissions) const override; qint64 fileSize(const FilePath &filePath) const override; qint64 bytesAvailable(const FilePath &filePath) const override; + QByteArray fileId(const FilePath &filePath) const override; void iterateDirectory( const FilePath &filePath, @@ -163,6 +165,7 @@ protected: bool setPermissions(const FilePath &filePath, QFile::Permissions) const override; qint64 fileSize(const FilePath &filePath) const override; qint64 bytesAvailable(const FilePath &filePath) const override; + QByteArray fileId(const FilePath &filePath) const override; void iterateDirectory( const FilePath &filePath, diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp index 57b3b197244..62dc52b2605 100644 --- a/src/libs/utils/environment.cpp +++ b/src/libs/utils/environment.cpp @@ -175,13 +175,7 @@ bool Environment::isSameExecutable(const QString &exe1, const QString &exe2) con for (const QString &i2 : exe2List) { const FilePath f1 = FilePath::fromString(i1); const FilePath f2 = FilePath::fromString(i2); - if (f1 == f2) - return true; - if (f1.needsDevice() != f2.needsDevice() || f1.scheme() != f2.scheme()) - return false; - if (f1.resolveSymlinks() == f2.resolveSymlinks()) - return true; - if (FileUtils::fileId(f1) == FileUtils::fileId(f2)) + if (f1.isSameFile(f2)) return true; } } diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 645ded358e5..057ed05847b 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -515,6 +515,25 @@ bool FilePath::isSameDevice(const FilePath &other) const return s_deviceHooks.isSameDevice(*this, other); } +bool FilePath::isSameFile(const FilePath &other) const +{ + if (*this == other) + return true; + + if (!isSameDevice(other)) + return false; + + const QByteArray fileId = fileAccess()->fileId(*this); + const QByteArray otherFileId = fileAccess()->fileId(other); + if (fileId.isEmpty() || otherFileId.isEmpty()) + return false; + + if (fileId == otherFileId) + return true; + + return false; +} + /// \returns an empty FilePath if this is not a symbolic linl FilePath FilePath::symLinkTarget() const { diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h index 0cc9455eef7..97e5752ccf8 100644 --- a/src/libs/utils/filepath.h +++ b/src/libs/utils/filepath.h @@ -207,6 +207,7 @@ public: bool needsDevice() const; bool isSameDevice(const FilePath &other) const; + bool isSameFile(const FilePath &other) const; [[nodiscard]] QFileInfo toFileInfo() const; [[nodiscard]] static FilePath fromFileInfo(const QFileInfo &info); diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index 989f6a3fcc9..ce5ac3a2497 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -5,7 +5,6 @@ #include "savefile.h" #include "algorithm.h" -#include "commandline.h" #include "qtcassert.h" #include "hostosinfo.h" @@ -15,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -25,7 +23,6 @@ #ifdef QT_GUI_LIB #include -#include #include #endif @@ -37,7 +34,7 @@ #include #endif -#ifdef Q_OS_OSX +#ifdef Q_OS_MACOS #include "fileutils_mac.h" #endif @@ -337,47 +334,6 @@ FilePaths FileUtils::CopyAskingForOverwrite::files() const } #endif // QT_GUI_LIB -// Copied from qfilesystemengine_win.cpp -#ifdef Q_OS_WIN - -// File ID for Windows up to version 7. -static inline QByteArray fileIdWin7(HANDLE handle) -{ - BY_HANDLE_FILE_INFORMATION info; - if (GetFileInformationByHandle(handle, &info)) { - char buffer[sizeof "01234567:0123456701234567\0"]; - qsnprintf(buffer, sizeof(buffer), "%lx:%08lx%08lx", - info.dwVolumeSerialNumber, - info.nFileIndexHigh, - info.nFileIndexLow); - return QByteArray(buffer); - } - return QByteArray(); -} - -// File ID for Windows starting from version 8. -static QByteArray fileIdWin8(HANDLE handle) -{ - QByteArray result; - FILE_ID_INFO infoEx; - if (GetFileInformationByHandleEx(handle, - static_cast(18), // FileIdInfo in Windows 8 - &infoEx, sizeof(FILE_ID_INFO))) { - result = QByteArray::number(infoEx.VolumeSerialNumber, 16); - result += ':'; - // Note: MinGW-64's definition of FILE_ID_128 differs from the MSVC one. - result += QByteArray(reinterpret_cast(&infoEx.FileId), int(sizeof(infoEx.FileId))).toHex(); - } - return result; -} - -static QByteArray fileIdWin(HANDLE fHandle) -{ - return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8 ? - fileIdWin8(HANDLE(fHandle)) : fileIdWin7(HANDLE(fHandle)); -} -#endif - FilePath FileUtils::commonPath(const FilePaths &paths) { if (paths.isEmpty()) @@ -424,33 +380,6 @@ FilePath FileUtils::commonPath(const FilePaths &paths) return result; } -QByteArray FileUtils::fileId(const FilePath &fileName) -{ - QByteArray result; - -#ifdef Q_OS_WIN - const HANDLE handle = - CreateFile((wchar_t*)fileName.toUserOutput().utf16(), 0, - FILE_SHARE_READ, NULL, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (handle != INVALID_HANDLE_VALUE) { - result = fileIdWin(handle); - CloseHandle(handle); - } -#else // Copied from qfilesystemengine_unix.cpp - if (Q_UNLIKELY(fileName.isEmpty())) - return result; - - QT_STATBUF statResult; - if (QT_STAT(fileName.toString().toLocal8Bit().constData(), &statResult)) - return result; - result = QByteArray::number(quint64(statResult.st_dev), 16); - result += ':'; - result += QByteArray::number(quint64(statResult.st_ino)); -#endif - return result; -} - #ifdef Q_OS_WIN template <> void withNtfsPermissions(const std::function &task) diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index 76ad91efbc8..bbeb906ee34 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -81,7 +81,6 @@ public: static bool isAbsolutePath(const QString &fileName) { return !isRelativePath(fileName); } static FilePath commonPath(const FilePath &oldCommonPath, const FilePath &fileName); static FilePath commonPath(const FilePaths &paths); - static QByteArray fileId(const FilePath &fileName); static FilePath homePath(); static FilePaths toFilePathList(const QStringList &paths); diff --git a/tests/auto/utils/fileutils/tst_fileutils.cpp b/tests/auto/utils/fileutils/tst_fileutils.cpp index 0248fb44891..4d600a68c2f 100644 --- a/tests/auto/utils/fileutils/tst_fileutils.cpp +++ b/tests/auto/utils/fileutils/tst_fileutils.cpp @@ -101,6 +101,9 @@ private slots: void cleanPath_data(); void cleanPath(); + void isSameFile_data(); + void isSameFile(); + private: QTemporaryDir tempDir; QString rootPath; @@ -1114,6 +1117,48 @@ void tst_fileutils::cleanPath() QCOMPARE(cleaned, expected); } +void tst_fileutils::isSameFile_data() +{ + QTest::addColumn("left"); + QTest::addColumn("right"); + QTest::addColumn("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); +} + QTEST_GUILESS_MAIN(tst_fileutils) #include "tst_fileutils.moc" From 67ecfd2fdc830afed7074c04dd46ae0c8519d55f Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 13 Oct 2022 14:00:39 +0300 Subject: [PATCH 039/143] QmlDesigner: Add required import when creating nodes via 3D view Creating nodes via 3D view context menu now adds required import if it is missing. Fixes: QDS-7950 Change-Id: Ibd4faff9d494a232e0fbf0fd5db3690449516bf6 Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Bot --- .../qmldesigner/components/edit3d/edit3dwidget.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp index e05c3dd10a7..dfe8d8c1a72 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp @@ -36,6 +36,7 @@ #include "viewmanager.h" #include #include +#include #include #include @@ -231,13 +232,20 @@ void Edit3DWidget::updateCreateSubMenu(const QStringList &keys, void Edit3DWidget::onCreateAction() { QAction *action = qobject_cast(sender()); - if (!action) + if (!action || !m_view || !m_view->model()) return; m_view->executeInTransaction(__FUNCTION__, [&] { - int activeScene = m_view->rootModelNode().auxiliaryData("active3dScene@Internal").toInt(); + ItemLibraryEntry entry = m_nameToEntry.value(action->data().toString()); + Import import = Import::createLibraryImport(entry.requiredImport(), + QString::number(entry.majorVersion()) + + QLatin1Char('.') + + QString::number(entry.minorVersion())); + if (!m_view->model()->hasImport(import)) + m_view->model()->changeImports({import}, {}); - auto modelNode = QmlVisualNode::createQml3DNode(m_view, m_nameToEntry.value(action->data().toString()), + int activeScene = m_view->rootModelNode().auxiliaryData("active3dScene@Internal").toInt(); + auto modelNode = QmlVisualNode::createQml3DNode(m_view, entry, activeScene, m_contextMenuPos3d).modelNode(); QTC_ASSERT(modelNode.isValid(), return); m_view->setSelectedModelNode(modelNode); From b4d0db920a11500b3a29f6f0abaaf9a08aa6fa06 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Wed, 12 Oct 2022 11:49:50 +0200 Subject: [PATCH 040/143] Doc: Update instructions for writing documentation For new and updated images, use the display resolution 1920x1080. Change-Id: Ie7a83d23eacfa8ba9b78a4de4ee9046e838fa72c Reviewed-by: Alessandro Portale --- .../src/qtcreator-documentation.qdoc | 85 ++++++++++++------- 1 file changed, 54 insertions(+), 31 deletions(-) diff --git a/doc/qtcreatordev/src/qtcreator-documentation.qdoc b/doc/qtcreatordev/src/qtcreator-documentation.qdoc index 66edefce4c6..62c5c3a2c11 100644 --- a/doc/qtcreatordev/src/qtcreator-documentation.qdoc +++ b/doc/qtcreatordev/src/qtcreator-documentation.qdoc @@ -321,22 +321,26 @@ users, but always place example values also in the text. \list - \li Use the screen resolution of 1366x768 (available on the most + \li Use the screen resolution of 1920x1080 (available on the most commonly used screens, as of this writing). - - \li Use the aspect ratio of 16:9. - - \li Open the application in the maximum size on full screen. + \note Use display scaling 100%. \li Use your favorite tool to take the screen shot. \li Include only the part of the screen that you need (you can crop the - image also in the screen capture tool). + image also in the screen capture tool). In \QDS, close all + unnecessary views to avoid clutter. + + \li Do not scale or resize the images in the tool because it causes + reduced visual quality and bigger file size. Also, the CSS we use + online scales down images if needed (their width is larger than 800 + pixels). \li To highlight parts of the screen shot, use the images of numbers - that are stored in \c{doc/images/numbers} in the \QC repository. + that are stored in \c{qtcreator/doc/qtcreator/images/numbers} in + the \QC repository. - \li Before you submit the images to the repository, optimize them to + \li Before you submit PNG images to the repository, optimize them to save space. \endlist @@ -344,9 +348,9 @@ You can use number icons in screenshots to highlight parts of the screenshot (instead of using red arrows or borders, or something similar). You can then - refer to the numbers in text. For and example, see the - \l{https://doc.qt.io/qt/topics-app-development.html}{Development Tools} - topic in the Qt reference documentation. + refer to the numbers in text. For an example, see the + \l{https://doc.qt.io/qtcreator/creator-quick-tour.html}{User Interface} + topic in the \QC Manual. This improves the consistency of the look and feel of Qt documentation, and eliminates the need to describe parts of the UI in the text because @@ -354,8 +358,8 @@ brackets. You can find a set of images that show the numbers from 1 to 10 in the - \c doc/images/numbers directory (or in the \c qtdoc module sources in - \c doc/images/numbers). + \c qtcreator/doc/qtcreator/images/numbers directory (or in the \c qtdoc + module sources in \c doc/images/numbers). To use the numbers, copy-paste the number images on the screenshot to the places that you want to refer to from text. @@ -380,6 +384,13 @@ recolors icons in \c qtcreator/doc/qtcreator/images/icons. Use the \c -docspath option to specify the path to another icon source directory. + For example, if you saved the new icons in \c {C:\iconconversions}, switch to + the \c {qtcreator\src\tools\icons} folder and enter: + + \badcode + recolordocsicons.py -docspath C:\iconconversions + \endcode + To run the script, you will need to install the following tools and add them to the PATH: @@ -389,20 +400,26 @@ \li optipng \endlist + \section2 Saving Images + + Save images in PNG or WebP format in the \QC project folder in the + \c doc/qtcreator/images or \c doc/qtdesignstudio/images folder. Binary + images can easily add megabytes to the Git + history. To keep the history as small as possible, the Git post-commit hooks + remind you to try to keep image file size below 50 kilobytes. To achieve this + goal, crop images so that only relevant information is visible in them. + + If your screenshot contains lots of colorful content or a photo, for example, + consider saving it in WebP format for a smaller image file size. + \section2 Optimizing Images - Save images in the PNG format in the \QC project folder in the - \c {doc/images} folder. Binary images can easily add megabytes to the Git - history. To keep the history as small as possible, the Git post-commit hooks - remind you to try to keep image size below 50 kilobytes. To achieve this - goal, crop images so that only relevant information is visible in them. - Before committing images, optimize them by using an image optimization tool. - - Optimization should not visibly reduce image quality. If it does, do not do - it. + Before committing PNG images, optimize them by using an image optimization + tool. Optimization should not visibly reduce image quality. If it does, try + saving the image as WebP instead. You can use a web service, such as \l{https://tinypng.com}, or an image - optimization tool to shrink the images. For example, you can use the Radical + optimization tool to shrink PNG images. For example, you can use the Radical Image Optimization Tool (RIOT) or OptiPNG on Windows, ImageOptim on \macos, or some other tool available on Linux. @@ -447,6 +464,16 @@ optipng -o 7 -strip all doc/images/ \endcode + \section2 Creating GIF Videos + + Sometimes it is easier to explain how something works by recording + a short GIF video. You can use any tool you like, for example + \l {https://www.screentogif.com/}{ScreenToGif}. GIF videos are typically + bigger than screenshots, so try to make them as short and to the point as + you can. + + Use the \c {\image} command to add GIF files to the documentation. + \section2 Linking to Youtube Videos You can use the \c {\youtube} macro to link to a video on Youtube. The HTML @@ -499,11 +526,7 @@ \section2 Setting Up Documentation Builds - For more information about setting up the build environment with a - self-built Qt if you do not want to build the whole Qt, see - \l{https://wiki.qt.io/Building_Qt_Documentation}{Building Qt Documentation} - on the Qt wiki. - + You can use an installed Qt version to build the documentation. The content and formatting of documentation are separated in QDoc. The documentation configuration, style sheets, and templates have changed over time, so they differ between Qt and \QC versions. @@ -554,7 +577,7 @@ For example (all on one line): \badcode C:\dev\qtc-doc-build>cmake -DWITH_DOCS=ON - "-DCMAKE_PREFIX_PATH=C:\Qt\5.15.1\msvc2019_64" + "-DCMAKE_PREFIX_PATH=C:\Qt\6.4.0\msvc2019_64" C:\dev\qtc-super\qtcreator \endcode \li To also build Extending \QC Manual, add the following option: @@ -566,7 +589,7 @@ \badcode C:\dev\qtc-doc-build>cmake -DWITH_DOCS=ON -DBUILD_DEVELOPER_DOCS=ON "-DCMAKE_MODULE_PATH=C:\dev\tqtc-plugin-qtquickdesigner\studiodata\branding" - "-DCMAKE_PREFIX_PATH=C:\Qt\5.15.1\msvc2019_64" + "-DCMAKE_PREFIX_PATH=C:\Qt\6.4.0\msvc2019_64" C:\dev\qtc-super\qtcreator \endcode \li To build the docs using the online style, use the following option @@ -578,7 +601,7 @@ C:\dev\qtc-doc-build>cmake -DWITH_ONLINE_DOCS=ON -DBUILD_DEVELOPER_DOCS=ON "-DCMAKE_MODULE_PATH=C:\dev\tqtc-plugin-qtquickdesigner\studiodata\branding" - "-DCMAKE_PREFIX_PATH=C:\Qt\5.15.1\msvc2019_64" + "-DCMAKE_PREFIX_PATH=C:\Qt\6.4.0\msvc2019_64" C:\dev\qtc-super\qtcreator \endcode \note If you already ran CMake \c {-DWITH_DOCS=ON} in a folder and From b2dbc0965c5a5a49c6770674458273f26e453137 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Fri, 7 Oct 2022 15:44:06 +0200 Subject: [PATCH 041/143] Editor: Fix double click in editor behind content Fixes: QTCREATORBUG-28083 Change-Id: Ie198c2f1c9911ec85ee555ff1058d2528f9e6b7d Reviewed-by: Christian Stenger --- src/plugins/texteditor/texteditor.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 2b55229c6ce..1cda9db17cd 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -5514,8 +5514,8 @@ void TextEditorWidget::mouseDoubleClickEvent(QMouseEvent *e) } } - QTextCursor oldCursor = multiTextCursor().mainCursor(); - const int oldPosition = oldCursor.position(); + QTextCursor eventCursor = cursorForPosition(QPoint(e->pos().x(), e->pos().y())); + const int eventDocumentPosition = eventCursor.position(); QPlainTextEdit::mouseDoubleClickEvent(e); @@ -5523,19 +5523,19 @@ void TextEditorWidget::mouseDoubleClickEvent(QMouseEvent *e) // event is triggered on a position that is inbetween two whitespaces this event selects the // previous word or nothing if the whitespaces are at the block start. Replace this behavior // with selecting the whitespaces starting from the previous word end to the next word. - const QChar character = characterAt(oldPosition); - const QChar prevCharacter = characterAt(oldPosition - 1); + const QChar character = characterAt(eventDocumentPosition); + const QChar prevCharacter = characterAt(eventDocumentPosition - 1); if (character.isSpace() && prevCharacter.isSpace()) { if (prevCharacter != QChar::ParagraphSeparator) { - oldCursor.movePosition(QTextCursor::PreviousWord); - oldCursor.movePosition(QTextCursor::EndOfWord); + eventCursor.movePosition(QTextCursor::PreviousWord); + eventCursor.movePosition(QTextCursor::EndOfWord); } else if (character == QChar::ParagraphSeparator) { return; // no special handling for empty lines } - oldCursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor); + eventCursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor); MultiTextCursor cursor = multiTextCursor(); - cursor.replaceMainCursor(oldCursor); + cursor.replaceMainCursor(eventCursor); setMultiTextCursor(cursor); } } From 033ec559b3e73459fb0c2a0d48d5a75dba2ce6d4 Mon Sep 17 00:00:00 2001 From: Mats Honkamaa Date: Thu, 13 Oct 2022 10:40:05 +0300 Subject: [PATCH 042/143] Doc: Improve wording for States and Connections pages These topic sets cover more than adding so I change "Adding" to "Working with". Task-number: QDS-7995 Change-Id: I240bce10c6cbfc04a686b719bd74cbd353d3de11 Reviewed-by: Leena Miettinen --- doc/qtdesignstudio/examples/doc/StateTransitions.qdoc | 4 ++-- doc/qtdesignstudio/examples/doc/loginui3.qdoc | 4 ++-- doc/qtdesignstudio/examples/doc/loginui4.qdoc | 2 +- doc/qtdesignstudio/examples/doc/sidemenu.qdoc | 2 +- doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc | 2 +- doc/qtdesignstudio/src/components/qtquick-buttons.qdoc | 2 +- .../src/components/qtquick-component-instances.qdoc | 2 +- doc/qtdesignstudio/src/components/qtquick-components.qdoc | 2 +- .../src/overviews/qtquick-creating-ui-logic.qdoc | 4 ++-- doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc | 4 ++-- doc/qtdesignstudio/src/qtdesignstudio-components.qdocinc | 2 +- doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc | 4 ++-- doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc | 4 ++-- doc/qtdesignstudio/src/views/qtquick-adding-dynamics.qdoc | 4 ++-- doc/qtdesignstudio/src/views/qtquick-connection-editor.qdoc | 2 +- doc/qtdesignstudio/src/views/qtquick-designer.qdoc | 4 ++-- doc/qtdesignstudio/src/views/qtquick-navigator.qdoc | 4 ++-- doc/qtdesignstudio/src/views/qtquick-properties-view.qdoc | 2 +- doc/qtdesignstudio/src/views/qtquick-properties.qdoc | 2 +- doc/qtdesignstudio/src/views/qtquick-states-view.qdoc | 4 ++-- doc/qtdesignstudio/src/views/qtquick-states.qdoc | 2 +- doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc | 2 +- 22 files changed, 32 insertions(+), 32 deletions(-) diff --git a/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc b/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc index 8c3ad88606f..619f51812d8 100644 --- a/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc +++ b/doc/qtdesignstudio/examples/doc/StateTransitions.qdoc @@ -26,7 +26,7 @@ /*! \page state-transition-animations.html \ingroup gstutorials - \sa States, {Transitions}, {Adding States} + \sa States, {Transitions}, {Working with States} \title Animated State Transitions \brief Illustrates how to create animated state transitions. @@ -34,7 +34,7 @@ \image animated-state-transitions.jpg The \e{Animated State Transitions} tutorial illustrates how you can animate - the transition between \l{Adding States}{states}. + the transition between \l{Working with States}{states}. The starting point of this tutorial is the Car Demo project, you can download it from diff --git a/doc/qtdesignstudio/examples/doc/loginui3.qdoc b/doc/qtdesignstudio/examples/doc/loginui3.qdoc index d4799d199d4..4cdca5f44b3 100644 --- a/doc/qtdesignstudio/examples/doc/loginui3.qdoc +++ b/doc/qtdesignstudio/examples/doc/loginui3.qdoc @@ -99,7 +99,7 @@ \section1 Using States to Simulate Page Changes - You will now add \l{Adding States}{states} to the UI to show and hide UI + You will now add \l{Working with States}{states} to the UI to show and hide UI components in the \uicontrol {2D} view, depending on the current page: \list 1 @@ -177,7 +177,7 @@ \section1 Learn More \section2 States - The \l{Adding States}{state} of a particular visual component is the set of + The \l{Working with States}{state} of a particular visual component is the set of information which describes how and where the individual parts of the visual component are displayed within it, and all the data associated with that state. Most visual components in a UI will have a limited number of states, diff --git a/doc/qtdesignstudio/examples/doc/loginui4.qdoc b/doc/qtdesignstudio/examples/doc/loginui4.qdoc index 35d8c1070e6..a82d9f502cf 100644 --- a/doc/qtdesignstudio/examples/doc/loginui4.qdoc +++ b/doc/qtdesignstudio/examples/doc/loginui4.qdoc @@ -262,7 +262,7 @@ \section1 Binding Animation to States - You will now bring back the \l{Adding States}{states} in the + You will now bring back the \l{Working with States}{states} in the \uicontrol States view and bind them to the animation settings in \uicontrol Timeline: diff --git a/doc/qtdesignstudio/examples/doc/sidemenu.qdoc b/doc/qtdesignstudio/examples/doc/sidemenu.qdoc index 08c4805deb1..c9168844d05 100644 --- a/doc/qtdesignstudio/examples/doc/sidemenu.qdoc +++ b/doc/qtdesignstudio/examples/doc/sidemenu.qdoc @@ -53,7 +53,7 @@ The button can have the following states: checked, hover, pressed, and normal. We construct the button using different images for the button - background, frame, and front. We then add \l{Adding States}{states} in + background, frame, and front. We then add \l{Working with States}{states} in the \l States view for each of the button states. In each state, we turn the visibility of the appropriate images on or off in the button properties, to change the appearance of the button. diff --git a/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc b/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc index c08bc016b1a..6f96e370764 100644 --- a/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc +++ b/doc/qtdesignstudio/examples/doc/washingMachineUI.qdoc @@ -188,7 +188,7 @@ in the top-left corner of the root component. Then, we open the \uicontrol States view to create the \e start, - \e settings, \e presets, and \e running \l{Adding States}{states} for + \e settings, \e presets, and \e running \l{Working with States}{states} for displaying a particular screen by selecting \uicontrol {Create New State}. \image washingmachineui-states.png "States view" diff --git a/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc b/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc index 54dacf9e62a..c5ade1f935f 100644 --- a/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-buttons.qdoc @@ -139,7 +139,7 @@ PNG file, as a border and a background. Use two border images and suitable graphics to change the appearance of - a button when it is clicked. You can use use \l{Adding States}{states} + a button when it is clicked. You can use use \l{Working with States}{states} to determine which image is visible depending on whether the mouse button is pressed down. You could add more images and states to change the appearance of the button depending on other mouse events, diff --git a/doc/qtdesignstudio/src/components/qtquick-component-instances.qdoc b/doc/qtdesignstudio/src/components/qtquick-component-instances.qdoc index d3a04f89be4..195874c3b66 100644 --- a/doc/qtdesignstudio/src/components/qtquick-component-instances.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-component-instances.qdoc @@ -65,7 +65,7 @@ \image qmldesigner-bindings.png "Connections view Bindings tab" \li Add states to apply sets of changes to the property values of one or several component instances in the \uicontrol States view. - For more information, see \l{Adding States}. + For more information, see \l{Working with States}. \li Animate the properties of component instances in the \uicontrol Timeline view. For more information, see \l{Creating Timeline Animations}. diff --git a/doc/qtdesignstudio/src/components/qtquick-components.qdoc b/doc/qtdesignstudio/src/components/qtquick-components.qdoc index 7ea137592ac..7a8c01faa3c 100644 --- a/doc/qtdesignstudio/src/components/qtquick-components.qdoc +++ b/doc/qtdesignstudio/src/components/qtquick-components.qdoc @@ -85,7 +85,7 @@ your UI. \li Use as few components as necessary. To minimize the number of components, use \l{Adding Property Aliases}{alias properties} and - \l{Adding States}{states} to create the differences in your + \l{Working with States}{states} to create the differences in your component instances. We recommend reusing components instead of duplicating them, so the components do not need to be processed as completely new component types. This reduces loading diff --git a/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc b/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc index 561eb8c62c3..8774a8ea674 100644 --- a/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc +++ b/doc/qtdesignstudio/src/overviews/qtquick-creating-ui-logic.qdoc @@ -54,7 +54,7 @@ exported as a public property of the relevant component. For example, a speedometer should have a property for speed to which the UI is bound. - You can declare various \l{Adding States}{UI states} that describe how + You can declare various \l{Working with States}{UI states} that describe how property values change from a base state. States can be a useful way of organizing your UI logic. You can associate transitions with components to define how their properties will animate when they change due to a @@ -122,7 +122,7 @@ \li \l{Adding Property Aliases} \row \li Referencing a state from within a specific component - \li \l{Adding States} + \li \l{Working with States} \row \li Switching to a state when a particular property changes \li \l{Applying States} diff --git a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc index 4b6cfe13777..f57b5870671 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-app-flows.qdoc @@ -71,7 +71,7 @@ \uicontrol {Flow Decision} components from \uicontrol Components > \uicontrol {Flow View}, as described in \l{Simulating Conditions}. - \li Use \l{Adding States}{states} in flows to modify the appearance + \li Use \l{Working with States}{states} in flows to modify the appearance of components on screens in response to user interaction, as described in \l{Applying States in Flows}. \li Use \uicontrol {Flow Wildcard} components from @@ -642,7 +642,7 @@ \title Applying States in Flows - You can use \l{Adding States}{states} in flows to modify the appearance + You can use \l{Working with States}{states} in flows to modify the appearance of \l{glossary-component}{components} in flow items in response to user interaction, for example. For this purpose, you use the \uicontrol {Flow Item} components available in diff --git a/doc/qtdesignstudio/src/qtdesignstudio-components.qdocinc b/doc/qtdesignstudio/src/qtdesignstudio-components.qdocinc index 330b2ee7be9..8b378e90955 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-components.qdocinc +++ b/doc/qtdesignstudio/src/qtdesignstudio-components.qdocinc @@ -50,7 +50,7 @@ \endtable You can edit the properties of the controls in all the preset - \l{Adding States}{states} to apply your own style to them. + \l{Working with States}{states} to apply your own style to them. \note For buttons and check boxes, you can disable the misbehaving hover effects by selecting \l Properties > \uicontrol Control, and then disabling diff --git a/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc index 15026d2c519..cd9f15bb46e 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-terms.qdoc @@ -143,7 +143,7 @@ \list \li \l{Connections} - \li \l{Adding Connections} + \li \l{Working with Connections} \endlist \section1 Device @@ -271,7 +271,7 @@ \list \li \l{States} - \li \l{Adding States} + \li \l{Working with States} \endlist \section1 Transition diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index bcccd0bf399..11ff34f21a4 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -140,13 +140,13 @@ \endlist \li \l{Dynamic Behaviors} \list - \li \l{Adding Connections} + \li \l{Working with Connections} \list \li\l{Connecting Components to Signals} \li\l{Adding Bindings Between Properties} \li\l{Specifying Custom Properties} \endlist - \li \l{Adding States} + \li \l{Working with States} \endlist \li \l{Validating with Target Hardware} \list diff --git a/doc/qtdesignstudio/src/views/qtquick-adding-dynamics.qdoc b/doc/qtdesignstudio/src/views/qtquick-adding-dynamics.qdoc index 274c205c60b..93fcf16bc34 100644 --- a/doc/qtdesignstudio/src/views/qtquick-adding-dynamics.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-adding-dynamics.qdoc @@ -35,7 +35,7 @@ when the values of other components or the UI state change. \list - \li \l {Adding Connections} + \li \l {Working with Connections} You can create connections between the UI components and the application to enable them to communicate with each other. For @@ -47,7 +47,7 @@ binding their properties together. This way, when the value of a property changes in a parent component, it can be automatically changed in all the child components, for example. - \li \l {Adding States} + \li \l {Working with States} You can declare various UI states that describe how component properties change from a base state. Therefore, states can be diff --git a/doc/qtdesignstudio/src/views/qtquick-connection-editor.qdoc b/doc/qtdesignstudio/src/views/qtquick-connection-editor.qdoc index 9c8360f5659..ce30db368f9 100644 --- a/doc/qtdesignstudio/src/views/qtquick-connection-editor.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-connection-editor.qdoc @@ -28,7 +28,7 @@ \previouspage qtquick-adding-dynamics.html \nextpage quick-signals.html - \title Adding Connections + \title Working with Connections \list \li \l{Connecting Components to Signals} diff --git a/doc/qtdesignstudio/src/views/qtquick-designer.qdoc b/doc/qtdesignstudio/src/views/qtquick-designer.qdoc index 3e05138e5ee..d1e846a3643 100644 --- a/doc/qtdesignstudio/src/views/qtquick-designer.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-designer.qdoc @@ -97,14 +97,14 @@ \li \l{Connections} \li Enables you to add functionality to the UI by creating connections between components, signals, and component properties. - \li \l{Adding Connections} + \li \l{Working with Connections} \row \li \l States \li Displays the different states that can be applied to a component. Typically, states describe UI configurations, such as the visibility and behavior of components and the available user actions. - \li \l{Adding States} + \li \l{Working with States} \row \li \l{Transitions} \li Enables you to make movement between states smooth by animating diff --git a/doc/qtdesignstudio/src/views/qtquick-navigator.qdoc b/doc/qtdesignstudio/src/views/qtquick-navigator.qdoc index c2e40b729a0..ce3cb0f27c1 100644 --- a/doc/qtdesignstudio/src/views/qtquick-navigator.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-navigator.qdoc @@ -146,7 +146,7 @@ the \l {3D} view nor access their properties in \uicontrol Properties. - If you attempt to \l{Adding States}{remove a state} that changes the + If you attempt to \l{Working with States}{remove a state} that changes the properties of a locked component, you are prompted to confirm the removal. If you have \l{Editing Animation Curves}{added easing curves} to keyframe @@ -234,7 +234,7 @@ \image qmldesigner-export-item.png You can then use the property alias in other components to - \l{Adding Connections}{create connections} to this component. + \l{Working with Connections}{create connections} to this component. \section1 Moving Within Components diff --git a/doc/qtdesignstudio/src/views/qtquick-properties-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-properties-view.qdoc index 8eb86df3216..7f5f694063d 100644 --- a/doc/qtdesignstudio/src/views/qtquick-properties-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-properties-view.qdoc @@ -108,7 +108,7 @@ The default values of properties are displayed in white color, while the values that you specify explicitly are highlighted with blue color. In - addition, property changes in \l{Adding States}{states} are highlighted + addition, property changes in \l{Working with States}{states} are highlighted with blue. This allows you to easily see which values are set in the component diff --git a/doc/qtdesignstudio/src/views/qtquick-properties.qdoc b/doc/qtdesignstudio/src/views/qtquick-properties.qdoc index a80201ea3ad..6b10563ccad 100644 --- a/doc/qtdesignstudio/src/views/qtquick-properties.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-properties.qdoc @@ -175,7 +175,7 @@ If the \uicontrol Clip check box is selected, the component and its children are clipped to the bounding rectangle of the component. - in the \uicontrol State field, select the \l{Adding States}{state} to + in the \uicontrol State field, select the \l{Working with States}{state} to change the value of a property in that state. \section1 Picking Colors diff --git a/doc/qtdesignstudio/src/views/qtquick-states-view.qdoc b/doc/qtdesignstudio/src/views/qtquick-states-view.qdoc index d5894641dbb..fe83102f999 100644 --- a/doc/qtdesignstudio/src/views/qtquick-states-view.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-states-view.qdoc @@ -30,8 +30,8 @@ \title States - The \uicontrol States view displays the different \l{Adding States}{states} - of a UI. + The \uicontrol States view displays the different + \l{Working with States}{states} of a UI. \image qmldesigner-transitions.png "States view" diff --git a/doc/qtdesignstudio/src/views/qtquick-states.qdoc b/doc/qtdesignstudio/src/views/qtquick-states.qdoc index 0ff4f27105e..b92146a7927 100644 --- a/doc/qtdesignstudio/src/views/qtquick-states.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-states.qdoc @@ -33,7 +33,7 @@ \nextpage exporting-3d-assets.html \endif - \title Adding States + \title Working with States You can define states for components and component instances in the \l States view by selecting \inlineimage icons/plus.png diff --git a/doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc b/doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc index 72e0217b2b6..55b5c58cfdd 100644 --- a/doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc +++ b/doc/qtdesignstudio/src/views/qtquick-transition-editor.qdoc @@ -34,7 +34,7 @@ \uicontrol {Transitions} to animate the changes between states. - First, you need to \l{Adding States}{add states} in the \l States view + First, you need to \l{Working with States}{add states} in the \l States view and \l{Specifying Component Properties}{edit some properties} that can be animated, such as colors or numbers, in the \l Properties view. For example, you can animate the changes in the position of a component. From f555a54f5a654606a2fb50e2829eef855960972c Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Thu, 13 Oct 2022 13:32:21 +0200 Subject: [PATCH 043/143] Docker: Detect qt6 in ubuntu container Installing Qt 6 via the distribution packages has a misconfigured qtchooser. To allow us to find Qt6 on these systems, we directly search for "qmake6" as well. Change-Id: I43f190bd2c69a0ea818308ff47bb6d9f2968e12f Reviewed-by: hjk --- src/plugins/docker/kitdetector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/docker/kitdetector.cpp b/src/plugins/docker/kitdetector.cpp index 18df41e8bae..252bd76d23a 100644 --- a/src/plugins/docker/kitdetector.cpp +++ b/src/plugins/docker/kitdetector.cpp @@ -233,7 +233,7 @@ QtVersions KitDetectorPrivate::autoDetectQtVersions() const emit q->logOutput(ProjectExplorer::Tr::tr("Searching for qmake executables...")); - const QStringList candidates = {"qmake-qt6", "qmake-qt5", "qmake"}; + const QStringList candidates = {"qmake6", "qmake-qt6", "qmake-qt5", "qmake"}; for (const FilePath &searchPath : m_searchPaths) { searchPath.iterateDirectory(handleQmake, {candidates, From 0377f0f2ac74843b6fcf245a3edb206e640b3650 Mon Sep 17 00:00:00 2001 From: Rainer Keller Date: Thu, 13 Oct 2022 14:08:59 +0200 Subject: [PATCH 044/143] McuSupport: Text replacement is always empty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The replacement for %1 is always empty and does not read nice. Change-Id: I5d23aa3dfc2a8d7365e113328f13dfd8ce7a946b Reviewed-by: Dawid Śliwa Reviewed-by: Sivert Krøvel Reviewed-by: Yasser Grimes Reviewed-by: hjk --- src/plugins/mcusupport/mcusupportoptionspage.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/mcusupport/mcusupportoptionspage.cpp b/src/plugins/mcusupport/mcusupportoptionspage.cpp index f4a794e4133..8bdb43ef65a 100644 --- a/src/plugins/mcusupport/mcusupportoptionspage.cpp +++ b/src/plugins/mcusupport/mcusupportoptionspage.cpp @@ -94,8 +94,7 @@ McuSupportOptionsWidget::McuSupportOptionsWidget(McuSupportOptions &options, } { - m_mcuTargetsGroupBox = new QGroupBox( - tr("Targets supported by the %1").arg(m_qtForMCUsSdkGroupBox->title())); + m_mcuTargetsGroupBox = new QGroupBox(tr("Supported targets")); m_mcuTargetsGroupBox->setFlat(true); mainLayout->addWidget(m_mcuTargetsGroupBox); m_mcuTargetsComboBox = new QComboBox; From 9f0dcd71c474e528cd3cf59856d72c0c6246da3d Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 11 Oct 2022 10:54:51 +0200 Subject: [PATCH 045/143] Squish: Adapt to ui changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ie7cb2a80a5edfa135a1b5d1b44c365a5e6cf408b Reviewed-by: Robert Löhning --- tests/system/objects.map | 2 +- tests/system/shared/debugger.py | 2 +- tests/system/suite_APTW/tst_APTW03/test.py | 2 +- tests/system/suite_debugger/tst_cli_output_console/test.py | 3 +-- tests/system/suite_debugger/tst_debug_empty_main/test.py | 2 +- tests/system/suite_debugger/tst_simple_analyze/test.py | 4 ++-- tests/system/suite_debugger/tst_simple_debug/test.py | 2 +- tests/system/suite_general/tst_build_speedcrunch/test.py | 2 +- tests/system/suite_general/tst_cmake_speedcrunch/test.py | 2 +- 9 files changed, 10 insertions(+), 11 deletions(-) diff --git a/tests/system/objects.map b/tests/system/objects.map index 93c2c8a5af2..5329fe8c0f9 100644 --- a/tests/system/objects.map +++ b/tests/system/objects.map @@ -214,7 +214,7 @@ :qt_tabwidget_stackedwidget_QScrollArea {container=':Options.qt_tabwidget_stackedwidget_QStackedWidget' type='QScrollArea' unnamed='1' visible='1'} :qt_tabwidget_stackedwidget_QWidget {container=':Options.qt_tabwidget_stackedwidget_QStackedWidget' type='QWidget' unnamed='1' visible='1'} :qtdirList_QTreeView {container=':qt_tabwidget_stackedwidget_QScrollArea' name='qtdirList' type='QTreeView' visible='1'} -:scrollArea.Details_Utils::DetailsButton {text='Details' type='Utils::DetailsButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} +:scrollArea.Details_Utils::DetailsButton {text='Details' type='QToolButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :scrollArea.Edit build configuration:_QComboBox {leftWidget=':scrollArea.Edit build configuration:_QLabel' type='QComboBox' unnamed='1' visible='1'} :scrollArea.Edit build configuration:_QLabel {text='Edit build configuration:' type='QLabel' unnamed='1' visible='1'} :scrollArea.Library not available_QLabel {name='qmlDebuggingWarningText' text?='Library not available*' type='QLabel' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} diff --git a/tests/system/shared/debugger.py b/tests/system/shared/debugger.py index 338d83cd758..2c4a44ae987 100644 --- a/tests/system/shared/debugger.py +++ b/tests/system/shared/debugger.py @@ -32,7 +32,7 @@ def handleDebuggerWarnings(config, isMsvcBuild=False): def takeDebuggerLog(): invokeMenuItem("View", "Views", "Global Debugger Log") debuggerLogWindow = waitForObject("{container=':DebugModeWidget.Debugger Log_QDockWidget' " - "type='Debugger::Internal::DebuggerPane' unnamed='1' visible='1'}") + "type='QPlainTextEdit' unnamed='1' visible='1'}") debuggerLog = str(debuggerLogWindow.plainText) mouseClick(debuggerLogWindow) invokeContextMenuItem(debuggerLogWindow, "Clear Contents") diff --git a/tests/system/suite_APTW/tst_APTW03/test.py b/tests/system/suite_APTW/tst_APTW03/test.py index 1202558a223..246dc37607e 100644 --- a/tests/system/suite_APTW/tst_APTW03/test.py +++ b/tests/system/suite_APTW/tst_APTW03/test.py @@ -112,7 +112,7 @@ def main(): invokeContextMenuItem(editor, "Toggle Comment Selection") virtualFunctionsAdded = True invokeMenuItem('File', 'Save All') - selectFromLocator("t rebuild", "Rebuild (Rebuild Project)") + selectFromLocator("t rebuild", "Rebuild Project") waitForCompile(10000) checkCompile() diff --git a/tests/system/suite_debugger/tst_cli_output_console/test.py b/tests/system/suite_debugger/tst_cli_output_console/test.py index 20db24d1866..95ccc298188 100644 --- a/tests/system/suite_debugger/tst_cli_output_console/test.py +++ b/tests/system/suite_debugger/tst_cli_output_console/test.py @@ -44,8 +44,7 @@ def main(): test.log("Running application") setRunInTerminal(kit, False) clickButton(waitForObject(":*Qt Creator.Run_Core::Internal::FancyToolButton")) - outputButton = waitForObject(":Qt Creator_AppOutput_Core::Internal::OutputPaneToggleButton") - waitFor("outputButton.checked", 20000) # Not ensureChecked(), avoid race condition + ensureChecked(":Qt Creator_AppOutput_Core::Internal::OutputPaneToggleButton") outputWindow = waitForObject(":Qt Creator_Core::OutputWindow") waitFor("'exited with code' in str(outputWindow.plainText) or \ 'The program has unexpectedly finished' in str(outputWindow.plainText)", 20000) diff --git a/tests/system/suite_debugger/tst_debug_empty_main/test.py b/tests/system/suite_debugger/tst_debug_empty_main/test.py index 92d79ca2288..0bbf7fa755b 100644 --- a/tests/system/suite_debugger/tst_debug_empty_main/test.py +++ b/tests/system/suite_debugger/tst_debug_empty_main/test.py @@ -78,7 +78,7 @@ def performDebugging(projectName): test.log("Selecting '%s' as build config" % config) verifyBuildConfig(kit, config, True, True, buildSystem="qmake") waitForObject(":*Qt Creator.Build Project_Core::Internal::FancyToolButton") - selectFromLocator("t rebuild", "Rebuild (Rebuild All Projects)") + selectFromLocator("t rebuild", "Rebuild All Projects") waitForCompile() isMsvc = isMsvcConfig(kit) clickButton(waitForObject(":*Qt Creator.Start Debugging_Core::Internal::FancyToolButton")) diff --git a/tests/system/suite_debugger/tst_simple_analyze/test.py b/tests/system/suite_debugger/tst_simple_analyze/test.py index 8cb3abec1a1..3bd04a6a6ee 100644 --- a/tests/system/suite_debugger/tst_simple_analyze/test.py +++ b/tests/system/suite_debugger/tst_simple_analyze/test.py @@ -42,11 +42,11 @@ def performTest(workingDir, projectName, availableConfigs): for kit, config in availableConfigs: # switching from MSVC to MinGW build will fail on the clean step of 'Rebuild All Projects' # because of differences between MSVC's and MinGW's Makefile (so clean before changing kit) - selectFromLocator("t clean", "Clean (Clean Project)") + selectFromLocator("t clean", "Clean Project") verifyBuildConfig(kit, config, True, True, True) test.log("Selected kit '%s'" % Targets.getStringForTarget(kit)) # explicitly build before start debugging for adding the executable as allowed program to WinFW - selectFromLocator("t rebuild", "Rebuild (Rebuild All Projects)") + selectFromLocator("t rebuild", "Rebuild All Projects") waitForCompile() if not checkCompile(): test.fatal("Compile had errors... Skipping current build config") diff --git a/tests/system/suite_debugger/tst_simple_debug/test.py b/tests/system/suite_debugger/tst_simple_debug/test.py index 7b3c166028e..4cfbaa30d6b 100644 --- a/tests/system/suite_debugger/tst_simple_debug/test.py +++ b/tests/system/suite_debugger/tst_simple_debug/test.py @@ -37,7 +37,7 @@ def main(): test.log("Selecting '%s' as build config" % config) verifyBuildConfig(kit, config, True, True, True) # explicitly build before start debugging for adding the executable as allowed program to WinFW - selectFromLocator("t rebuild", "Rebuild (Rebuild All Projects)") + selectFromLocator("t rebuild", "Rebuild All Projects") waitForCompile(300000) if not checkCompile(): test.fatal("Compile had errors... Skipping current build config") diff --git a/tests/system/suite_general/tst_build_speedcrunch/test.py b/tests/system/suite_general/tst_build_speedcrunch/test.py index 0618f058914..5a606a837d7 100644 --- a/tests/system/suite_general/tst_build_speedcrunch/test.py +++ b/tests/system/suite_general/tst_build_speedcrunch/test.py @@ -36,7 +36,7 @@ def main(): test.log("Testing build configuration: " + config) invokeMenuItem("Build", "Run qmake") waitForCompile() - selectFromLocator("t rebuild", "Rebuild (Rebuild All Projects)") + selectFromLocator("t rebuild", "Rebuild All Projects") waitForCompile(300000) checkCompile() checkLastBuild() diff --git a/tests/system/suite_general/tst_cmake_speedcrunch/test.py b/tests/system/suite_general/tst_cmake_speedcrunch/test.py index 380993e233d..b74c3d5cc9b 100644 --- a/tests/system/suite_general/tst_cmake_speedcrunch/test.py +++ b/tests/system/suite_general/tst_cmake_speedcrunch/test.py @@ -44,7 +44,7 @@ def main(): compareProjectTree(naviTreeView % "speedcrunch( \[\S+\])?", treeFile) # Invoke a rebuild of the application - selectFromLocator("t rebuild", "Rebuild (Rebuild All Projects)") + selectFromLocator("t rebuild", "Rebuild All Projects") # Wait for, and test if the build succeeded waitForCompile(300000) From 991cd546392fc4a3ea6765792b579f2a5aca58d1 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Thu, 13 Oct 2022 14:56:22 +0200 Subject: [PATCH 046/143] Docker: Allow changing useLocalUidGid on macOS Change-Id: Ic09b292df6ffad8fbe2257a4f5fdb5fd44cd6e4f Reviewed-by: hjk --- src/plugins/docker/dockerdevicewidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/docker/dockerdevicewidget.cpp b/src/plugins/docker/dockerdevicewidget.cpp index 26a349a56bb..ce88373306a 100644 --- a/src/plugins/docker/dockerdevicewidget.cpp +++ b/src/plugins/docker/dockerdevicewidget.cpp @@ -81,7 +81,7 @@ DockerDeviceWidget::DockerDeviceWidget(const IDevice::Ptr &device) m_runAsOutsideUser->setToolTip(Tr::tr("Uses user ID and group ID of the user running Qt Creator " "in the docker container.")); m_runAsOutsideUser->setChecked(m_data.useLocalUidGid); - m_runAsOutsideUser->setEnabled(HostOsInfo::isLinuxHost()); + m_runAsOutsideUser->setEnabled(HostOsInfo::isAnyUnixHost()); connect(m_runAsOutsideUser, &QCheckBox::toggled, this, [this, dockerDevice](bool on) { m_data.useLocalUidGid = on; From 6becc304b886ac9f789060bd6a0bce6c0cb85e43 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 12 Oct 2022 10:39:03 +0200 Subject: [PATCH 047/143] Fix compiler warnings - private field 'm_postId' is not used - private field 'm_hostChecked' is not used - missing field 'AcrossEmptyLines' initializer Change-Id: I1e31c79e078bcb68d053202ad322299b92c4fa38 Reviewed-by: Christian Stenger Reviewed-by: Qt CI Bot --- src/plugins/clangformat/clangformatutils.cpp | 4 ++-- src/plugins/cpaster/pastebindotcomprotocol.h | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/plugins/clangformat/clangformatutils.cpp b/src/plugins/clangformat/clangformatutils.cpp index 31660de3392..dbc4a6a347e 100644 --- a/src/plugins/clangformat/clangformatutils.cpp +++ b/src/plugins/clangformat/clangformatutils.cpp @@ -34,8 +34,8 @@ clang::format::FormatStyle qtcStyle() style.AccessModifierOffset = -4; style.AlignAfterOpenBracket = FormatStyle::BAS_Align; #if LLVM_VERSION_MAJOR >= 15 - style.AlignConsecutiveAssignments = {false}; - style.AlignConsecutiveDeclarations = {false}; + style.AlignConsecutiveAssignments = {false, false, false, false, false}; + style.AlignConsecutiveDeclarations = {false, false, false, false, false}; #elif LLVM_VERSION_MAJOR >= 12 style.AlignConsecutiveAssignments = FormatStyle::ACS_None; style.AlignConsecutiveDeclarations = FormatStyle::ACS_None; diff --git a/src/plugins/cpaster/pastebindotcomprotocol.h b/src/plugins/cpaster/pastebindotcomprotocol.h index 740fbca7447..b17fc71729b 100644 --- a/src/plugins/cpaster/pastebindotcomprotocol.h +++ b/src/plugins/cpaster/pastebindotcomprotocol.h @@ -34,8 +34,6 @@ private: QNetworkReply *m_listReply = nullptr; QString m_fetchId; - int m_postId = -1; - bool m_hostChecked = false; }; } // CodePaster From 161fb4c44059e21e04514e3e5556849d6493c841 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Thu, 13 Oct 2022 15:12:21 +0200 Subject: [PATCH 048/143] UnifiedDiffEditor: Fix never ending spinner when no difference Change-Id: If0d646c760cdc8c9fb8e2481a7798eed925681b6 Reviewed-by: Cristian Adam --- src/plugins/diffeditor/unifieddiffeditorwidget.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp index e182417a831..b977c159af2 100644 --- a/src/plugins/diffeditor/unifieddiffeditorwidget.cpp +++ b/src/plugins/diffeditor/unifieddiffeditorwidget.cpp @@ -77,7 +77,6 @@ UnifiedDiffEditorWidget::~UnifiedDiffEditorWidget() void UnifiedDiffEditorWidget::setDocument(DiffEditorDocument *document) { - m_controller.setBusyShowing(true); m_controller.setDocument(document); clear(); setDiff(document ? document->diffFiles() : QList()); From 689ba1ead1d22de262d80178ad9ab0b33d4a5f35 Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 12 Oct 2022 15:13:45 +0200 Subject: [PATCH 049/143] Debugger: Fix internal FilePath dumper Amends 23e96dd6fe. Change-Id: Ia602f09e4138125d41cb455b847e8b85ad767782 Reviewed-by: Reviewed-by: Christian Stenger --- share/qtcreator/debugger/creatortypes.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/share/qtcreator/debugger/creatortypes.py b/share/qtcreator/debugger/creatortypes.py index 2e440d5d2bc..875cb1ad03a 100644 --- a/share/qtcreator/debugger/creatortypes.py +++ b/share/qtcreator/debugger/creatortypes.py @@ -199,22 +199,23 @@ def qdump__CPlusPlus__Internal__Value(d, value): def qdump__Utils__FilePath(d, value): data, path_len, scheme_len, host_len = d.split("{@QString}IHH", value) - if False: - scheme_enc = d.encodeString(scheme) - host_enc = d.encodeString(host) - elided, path_enc = d.encodeStringHelper(path, d.displayStringLimit) - val = "" + elided, enc = d.encodeStringHelper(data, d.displayStringLimit) + # enc is concatenated path + scheme + host + if scheme_len: + scheme_pos = path_len * 4 + host_pos = scheme_pos + scheme_len * 4 + path_enc = enc[0 : path_len * 4] + scheme_enc = enc[scheme_pos : scheme_pos + scheme_len * 4] + host_enc = enc[host_pos : host_pos + host_len * 4] slash = "2F00" dot = "2E00" colon = "3A00" - if len(scheme_enc): - val = scheme_enc + colon + slash + slash + host_enc - if not path_enc.startswith(slash): - val += slash + dot + slash + val = scheme_enc + colon + slash + slash + host_enc + if not path_enc.startswith(slash): + val += slash + dot + slash val += path_enc else: - elided, data_enc = d.encodeStringHelper(data, d.displayStringLimit) - val = data_enc + val = enc d.putValue(val, "utf16", elided=elided) d.putPlainChildren(value) From b6a766669751ec71cfdb026ad3dbd3b8d8be1c4e Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Thu, 13 Oct 2022 10:35:36 +0200 Subject: [PATCH 050/143] ClangCodeModel: Do not needlessly update the configuration ... of a document. This fixes the indexing progress bar appearing when opening a source file (unless it has editor defines set). Change-Id: Ia99b226abdb1d5a98a92b23eb4d7311611940b0a Reviewed-by: Qt CI Bot Reviewed-by: Reviewed-by: Christian Stenger --- src/plugins/clangcodemodel/clangdclient.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index 5c249fa2290..b6693adf689 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -736,6 +736,8 @@ void ClangdClient::updateParserConfig(const Utils::FilePath &filePath, const CppEditor::BaseEditorDocumentParser::Configuration &config) { // TODO: Also handle usePrecompiledHeaders? + // TODO: Should we write the editor defines into the json file? It seems strange + // that they should affect the index only while the file is open in the editor. const auto projectPart = !config.preferredProjectPartId.isEmpty() ? CppEditor::CppModelManager::instance()->projectPartForId( config.preferredProjectPartId) @@ -745,10 +747,15 @@ void ClangdClient::updateParserConfig(const Utils::FilePath &filePath, CppEditor::BaseEditorDocumentParser::Configuration fullConfig = config; fullConfig.preferredProjectPartId = projectPart->id(); - CppEditor::BaseEditorDocumentParser::Configuration &cachedConfig = d->parserConfigs[filePath]; - if (cachedConfig == fullConfig) + auto cachedConfig = d->parserConfigs.find(filePath); + if (cachedConfig == d->parserConfigs.end()) { + cachedConfig = d->parserConfigs.insert(filePath, fullConfig); + if (config.preferredProjectPartId.isEmpty() && config.editorDefines.isEmpty()) + return; + } else if (cachedConfig.value() == fullConfig) { return; - cachedConfig = fullConfig; + } + cachedConfig.value() = fullConfig; QJsonObject cdbChanges; const Utils::FilePath includeDir = CppEditor::ClangdSettings(d->settings).clangdIncludePath(); From a34125ae11ee391f301dc3f414e91e20f6b4da4e Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 11 Oct 2022 16:22:15 +0200 Subject: [PATCH 051/143] CMake/qmake: Fix target triple for iOS targets Neither cmake nor qmake know the full iOS compiler command line, so we have to construct the target triple for the code model ourselves. Fixes: QTCREATORBUG-28278 Change-Id: I6cac06f340e9388de5c86509a8df4ac00eef87cd Reviewed-by: Eike Ziller --- .../cmakeprojectmanager/cmakebuildsystem.cpp | 34 +++++++++---------- .../projectexplorer/rawprojectpart.cpp | 26 ++++++++++++++ src/plugins/projectexplorer/rawprojectpart.h | 8 +++++ .../qmakeprojectmanager/qmakeparsernodes.cpp | 1 + .../qmakeprojectmanager/qmakeparsernodes.h | 1 + .../qmakeprojectmanager/qmakeproject.cpp | 9 +++-- 6 files changed, 60 insertions(+), 19 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index 4b089dff6fc..d2239ccaa9e 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -607,24 +607,24 @@ void CMakeBuildSystem::updateProjectData() setError(errorMessage); qCDebug(cmakeBuildSystemLog) << "Raw project parts created." << errorMessage; - { - for (RawProjectPart &rpp : rpps) { - rpp.setQtVersion( - kitInfo.projectPartQtVersion); // TODO: Check if project actually uses Qt. - const QString includeFileBaseDir = buildConfiguration()->buildDirectory().toString(); - if (kitInfo.cxxToolChain) { - rpp.setFlagsForCxx({kitInfo.cxxToolChain, rpp.flagsForCxx.commandLineFlags, - includeFileBaseDir}); - } - if (kitInfo.cToolChain) { - rpp.setFlagsForC({kitInfo.cToolChain, rpp.flagsForC.commandLineFlags, - includeFileBaseDir}); - } - } - - m_cppCodeModelUpdater->update({p, kitInfo, buildConfiguration()->environment(), rpps}, - m_extraCompilers); + for (RawProjectPart &rpp : rpps) { + rpp.setQtVersion( + kitInfo.projectPartQtVersion); // TODO: Check if project actually uses Qt. + const QString includeFileBaseDir = buildConfiguration()->buildDirectory().toString(); + QStringList cxxFlags = rpp.flagsForCxx.commandLineFlags; + QStringList cFlags = rpp.flagsForC.commandLineFlags; + addTargetFlagForIos(cxxFlags, cFlags, this, [this] { + return m_configurationFromCMake.stringValueOf("CMAKE_OSX_DEPLOYMENT_TARGET"); + }); + if (kitInfo.cxxToolChain) + rpp.setFlagsForCxx({kitInfo.cxxToolChain, cxxFlags, includeFileBaseDir}); + if (kitInfo.cToolChain) + rpp.setFlagsForC({kitInfo.cToolChain, cFlags, includeFileBaseDir}); } + + m_cppCodeModelUpdater->update({p, kitInfo, buildConfiguration()->environment(), rpps}, + m_extraCompilers); + { const bool mergedHeaderPathsAndQmlImportPaths = kit()->value( QtSupport::KitHasMergedHeaderPathsWithQmlImportPaths::id(), false).toBool(); diff --git a/src/plugins/projectexplorer/rawprojectpart.cpp b/src/plugins/projectexplorer/rawprojectpart.cpp index 35962516171..1f7cea32c96 100644 --- a/src/plugins/projectexplorer/rawprojectpart.cpp +++ b/src/plugins/projectexplorer/rawprojectpart.cpp @@ -5,10 +5,12 @@ #include "abi.h" #include "buildconfiguration.h" +#include "buildsystem.h" #include "kitinformation.h" #include "project.h" #include "target.h" +#include #include namespace ProjectExplorer { @@ -191,4 +193,28 @@ ProjectUpdateInfo::ProjectUpdateInfo(Project *project, } } +// We do not get the -target flag from qmake or cmake on macOS; see QTCREATORBUG-28278. +void addTargetFlagForIos(QStringList &cFlags, QStringList &cxxFlags, const BuildSystem *bs, + const std::function &getDeploymentTarget) +{ + const Utils::Id deviceType = DeviceTypeKitAspect::deviceTypeId(bs->target()->kit()); + if (deviceType != Ios::Constants::IOS_DEVICE_TYPE + && deviceType != Ios::Constants::IOS_SIMULATOR_TYPE) { + return; + } + const bool isSim = deviceType == Ios::Constants::IOS_SIMULATOR_TYPE; + QString targetTriple(QLatin1String(isSim ? "x86_64" : "arm64")); + targetTriple.append("-apple-ios").append(getDeploymentTarget()); + if (isSim) + targetTriple.append("-simulator"); + const auto addTargetFlag = [&targetTriple](QStringList &flags) { + if (!flags.contains("-target") && !Utils::contains(flags, + [](const QString &flag) { return flag.startsWith("--target="); })) { + flags << "-target" << targetTriple; + } + }; + addTargetFlag(cxxFlags); + addTargetFlag(cFlags); +} + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/rawprojectpart.h b/src/plugins/projectexplorer/rawprojectpart.h index fa36dc6c4a8..e1895869294 100644 --- a/src/plugins/projectexplorer/rawprojectpart.h +++ b/src/plugins/projectexplorer/rawprojectpart.h @@ -21,9 +21,17 @@ namespace ProjectExplorer { +class BuildSystem; class Kit; class Project; +void PROJECTEXPLORER_EXPORT addTargetFlagForIos( + QStringList &cFlags, + QStringList &cxxFlags, + const BuildSystem *bs, + const std::function &getDeploymentTarget + ); + class PROJECTEXPLORER_EXPORT RawProjectPartFlags { public: diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp index 6df3ff16fe7..8467b62bd6b 100644 --- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp @@ -1557,6 +1557,7 @@ QmakeEvalResultPtr QmakeProFile::evaluate(const QmakeEvalInput &input) result->newVarValues[Variable::AndroidAbis] = exactReader->values(QLatin1String(Android::Constants::ANDROID_ABIS)); result->newVarValues[Variable::AndroidApplicationArgs] = exactReader->values(QLatin1String(Android::Constants::ANDROID_APPLICATION_ARGUMENTS)); result->newVarValues[Variable::AndroidExtraLibs] = exactReader->values(QLatin1String(Android::Constants::ANDROID_EXTRA_LIBS)); + result->newVarValues[Variable::IosDeploymentTarget] = exactReader->values("QMAKE_IOS_DEPLOYMENT_TARGET"); result->newVarValues[Variable::AppmanPackageDir] = exactReader->values(QLatin1String("AM_PACKAGE_DIR")); result->newVarValues[Variable::AppmanManifest] = exactReader->values(QLatin1String("AM_MANIFEST")); result->newVarValues[Variable::IsoIcons] = exactReader->values(QLatin1String("ISO_ICONS")); diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h index d481dda64ce..e0e6cdd1331 100644 --- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.h +++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.h @@ -80,6 +80,7 @@ enum class Variable { AndroidPackageSourceDir, AndroidExtraLibs, AndroidApplicationArgs, + IosDeploymentTarget, AppmanPackageDir, AppmanManifest, IsoIcons, diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index 605c4c19bb2..47adf657e9a 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -350,10 +350,15 @@ void QmakeBuildSystem::updateCppCodeModel() }; const QStringList extraCxxArgs = getExtraFlagsFromCompilerVar(Variable::QmakeCxx); + cxxArgs << extraCxxArgs; const QStringList extraCArgs = getExtraFlagsFromCompilerVar(Variable::QmakeCc); + cArgs << extraCArgs; + addTargetFlagForIos(cArgs, cxxArgs, this, [pro] { + return pro->variableValue(Variable::IosDeploymentTarget).join(QString()); + }); - rpp.setFlagsForCxx({kitInfo.cxxToolChain, cxxArgs << extraCxxArgs, includeFileBaseDir}); - rpp.setFlagsForC({kitInfo.cToolChain, cArgs << extraCArgs, includeFileBaseDir}); + rpp.setFlagsForCxx({kitInfo.cxxToolChain, cxxArgs, includeFileBaseDir}); + rpp.setFlagsForC({kitInfo.cToolChain, cArgs, includeFileBaseDir}); rpp.setMacros(ProjectExplorer::Macro::toMacros(pro->cxxDefines())); rpp.setPreCompiledHeaders(pro->variableValue(Variable::PrecompiledHeader)); rpp.setSelectedForBuilding(pro->includedInExactParse()); From 9f8243e5cc7296fe1ca700b4654aa2c160840f58 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Thu, 13 Oct 2022 10:21:57 +0200 Subject: [PATCH 052/143] Squish: Use callback instead of signals and slots ...when "querying" the server. There are other queries which need to get handled properly. Reduces maintenance as using a callback here allows to avoid unnecessary connects and drops the need of the disconnects. Change-Id: Ib1c2c2b5b20c84b7eee5503ad134f5ab0e18c5a6 Reviewed-by: David Schulz --- src/plugins/squish/squishfilehandler.cpp | 8 +++----- src/plugins/squish/squishsettings.cpp | 4 +--- src/plugins/squish/squishtools.cpp | 8 ++++++-- src/plugins/squish/squishtools.h | 6 ++++-- src/plugins/squish/squishwizardpages.cpp | 6 ++---- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/plugins/squish/squishfilehandler.cpp b/src/plugins/squish/squishfilehandler.cpp index 99ee316bf2b..115086bc742 100644 --- a/src/plugins/squish/squishfilehandler.cpp +++ b/src/plugins/squish/squishfilehandler.cpp @@ -74,17 +74,15 @@ public: setWindowTitle(Tr::tr("Recording Settings")); auto squishTools = SquishTools::instance(); - connect(squishTools, &SquishTools::queryFinished, this, - [this] (const QString &out, const QString &) { + QApplication::setOverrideCursor(Qt::WaitCursor); + + squishTools->queryServerSettings([this] (const QString &out, const QString &) { SquishServerSettings s; s.setFromXmlOutput(out); QApplication::restoreOverrideCursor(); for (const QString &app : s.mappedAuts.keys()) aut.addItem(app); }); - - QApplication::setOverrideCursor(Qt::WaitCursor); - squishTools->queryServerSettings(); } diff --git a/src/plugins/squish/squishsettings.cpp b/src/plugins/squish/squishsettings.cpp index d6dc68d91ae..17daef73f41 100644 --- a/src/plugins/squish/squishsettings.cpp +++ b/src/plugins/squish/squishsettings.cpp @@ -406,15 +406,13 @@ SquishServerSettingsWidget::SquishServerSettingsWidget(QWidget *parent) // query settings SquishTools *squishTools = SquishTools::instance(); - connect(squishTools, &SquishTools::queryFinished, this, - [this, progress] (const QString &out, const QString &) { + squishTools->queryServerSettings([this, progress] (const QString &out, const QString &) { m_serverSettings.setFromXmlOutput(out); m_originalSettings.setFromXmlOutput(out); repopulateApplicationView(); progress->hide(); setEnabled(true); }); - squishTools->queryServerSettings(); } void SquishServerSettingsWidget::repopulateApplicationView() diff --git a/src/plugins/squish/squishtools.cpp b/src/plugins/squish/squishtools.cpp index 614ac1f1e82..df63485fcc0 100644 --- a/src/plugins/squish/squishtools.cpp +++ b/src/plugins/squish/squishtools.cpp @@ -260,10 +260,12 @@ void SquishTools::runTestCases(const FilePath &suitePath, startSquishServer(RunTestRequested); } -void SquishTools::queryServerSettings() +void SquishTools::queryServerSettings(QueryCallback callback) { if (m_shutdownInitiated) return; + m_queryCallback = callback; + if (m_state != Idle) { QMessageBox::critical(Core::ICore::dialogParent(), Tr::tr("Error"), @@ -686,9 +688,11 @@ void SquishTools::onRunnerFinished() if (m_request == RunnerQueryRequested) { const QString error = m_licenseIssues ? Tr::tr("Could not get Squish license from server.") : QString(); - emit queryFinished(m_fullRunnerOutput, error); + if (m_queryCallback) + m_queryCallback(m_fullRunnerOutput, error); setState(RunnerStopped); m_fullRunnerOutput.clear(); + m_queryCallback = {}; return; } diff --git a/src/plugins/squish/squishtools.h b/src/plugins/squish/squishtools.h index 72825514e44..d9400f7ad07 100644 --- a/src/plugins/squish/squishtools.h +++ b/src/plugins/squish/squishtools.h @@ -61,12 +61,14 @@ public: Finished }; + using QueryCallback = std::function; + State state() const { return m_state; } void runTestCases(const Utils::FilePath &suitePath, const QStringList &testCases = QStringList()); void recordTestCase(const Utils::FilePath &suitePath, const QString &testCaseName, const SuiteConf &suiteConf); - void queryServerSettings(); + void queryServerSettings(QueryCallback callback); void writeServerSettingsChanges(const QList &changes); void requestExpansion(const QString &name); @@ -77,7 +79,6 @@ signals: void squishTestRunStarted(); void squishTestRunFinished(); void resultOutputCreated(const QByteArray &output); - void queryFinished(const QString &output, const QString &error); void configChangesFailed(QProcess::ProcessError error); void configChangesWritten(); void localsUpdated(const QString &output); @@ -161,6 +162,7 @@ private: QTimer *m_requestVarsTimer = nullptr; qint64 m_readResultsCount; int m_autId = 0; + QueryCallback m_queryCallback; bool m_shutdownInitiated = false; bool m_closeRunnerOnEndRecord = false; bool m_licenseIssues = false; diff --git a/src/plugins/squish/squishwizardpages.cpp b/src/plugins/squish/squishwizardpages.cpp index b40a037cf45..62535b5a3ce 100644 --- a/src/plugins/squish/squishwizardpages.cpp +++ b/src/plugins/squish/squishwizardpages.cpp @@ -126,8 +126,8 @@ void SquishToolkitsPage::fetchServerSettings() auto squishTools = SquishTools::instance(); QTC_ASSERT(squishTools, return); - connect(squishTools, &SquishTools::queryFinished, this, - [this] (const QString &out, const QString &error) { + QApplication::setOverrideCursor(Qt::WaitCursor); + squishTools->queryServerSettings([this](const QString &out, const QString &error) { SquishServerSettings s; s.setFromXmlOutput(out); QApplication::restoreOverrideCursor(); @@ -149,8 +149,6 @@ void SquishToolkitsPage::fetchServerSettings() m_errorLabel->setVisible(true); } }); - QApplication::setOverrideCursor(Qt::WaitCursor); - squishTools->queryServerSettings(); } /********************************* ScriptLanguagePage ********************************************/ From 541ddd1ff756fb9d8f6c6abe1734d40c813812f8 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 13 Oct 2022 14:14:51 +0200 Subject: [PATCH 053/143] RemoteLinux: Be explicit about the current rsync support We currently only transfer host->remote device (or back), not host->host or remote->remote. Inform the user if we hit an (currently) unsupported combination. Change-Id: Icd33414d7d0bc2b5db284a62b5d757989f1596c6 Reviewed-by: Christian Kandeler --- src/plugins/remotelinux/rsyncdeploystep.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/plugins/remotelinux/rsyncdeploystep.cpp b/src/plugins/remotelinux/rsyncdeploystep.cpp index ddd9079c125..1f6ae266282 100644 --- a/src/plugins/remotelinux/rsyncdeploystep.cpp +++ b/src/plugins/remotelinux/rsyncdeploystep.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -152,7 +153,13 @@ RsyncDeployStep::RsyncDeployStep(BuildStepList *bsl, Id id) BoolAspect::LabelPlacement::InExtraLabel); ignoreMissingFiles->setValue(false); - setInternalInitializer([service, flags, ignoreMissingFiles] { + 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(); From b2fb86f3afe896794efd0b7c8db5b0c6f4d1deb9 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 13 Oct 2022 14:20:34 +0200 Subject: [PATCH 054/143] RemoteLinux: Skip initial rsync step for remote build == run Change-Id: I4d02b666fe10adf38513939255314682222cc82e Reviewed-by: Christian Kandeler Reviewed-by: --- .../remotelinux/remotelinuxdeployconfiguration.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp index 0bcdd6682b0..721107afebb 100644 --- a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp +++ b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp @@ -40,8 +40,13 @@ RemoteLinuxDeployConfigurationFactory::RemoteLinuxDeployConfigurationFactory() addInitialStep(Constants::MakeInstallStepId, needsMakeInstall); addInitialStep(Constants::KillAppStepId); addInitialStep(Constants::RsyncDeployStepId, [](Target *target) { - auto device = DeviceKitAspect::device(target->kit()); - return device && device->extraData(Constants::SupportsRSync).toBool(); + 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(); }); addInitialStep(Constants::DirectUploadStepId, [](Target *target) { auto device = DeviceKitAspect::device(target->kit()); From 76ffb54e909267cdaea8b06fc893de234e6b3189 Mon Sep 17 00:00:00 2001 From: Ari Parkkila Date: Thu, 13 Oct 2022 10:13:05 +0300 Subject: [PATCH 055/143] Make rsync to be default deploy step in Boot2Qt plugin Change-Id: I69c4cfc0f5e564544e19327f16a9d8cc1fd0b133 Reviewed-by: hjk Reviewed-by: Samuli Piippo --- src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp b/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp index d5a3a766ed4..1eedc858a33 100644 --- a/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp +++ b/src/plugins/boot2qt/qdbdeployconfigurationfactory.cpp @@ -6,6 +6,7 @@ #include "qdbconstants.h" #include +#include "projectexplorer/devicesupport/idevice.h" #include #include #include @@ -31,7 +32,14 @@ QdbDeployConfigurationFactory::QdbDeployConfigurationFactory() && prj->hasMakeInstallEquivalent(); }); addInitialStep(Qdb::Constants::QdbStopApplicationStepId); - addInitialStep(RemoteLinux::Constants::DirectUploadStepId); + addInitialStep(RemoteLinux::Constants::RsyncDeployStepId, [](Target *target) { + auto device = DeviceKitAspect::device(target->kit()); + return device && device->extraData(RemoteLinux::Constants::SupportsRSync).toBool(); + }); + addInitialStep(RemoteLinux::Constants::DirectUploadStepId, [](Target *target) { + auto device = DeviceKitAspect::device(target->kit()); + return device && !device->extraData(RemoteLinux::Constants::SupportsRSync).toBool(); + }); } } // namespace Internal From fc24f5c26da171be89dc01512f5d974da02c1675 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Tue, 11 Oct 2022 10:24:57 +0200 Subject: [PATCH 056/143] LanguageClient: use separate variable to save shutdown state The LanguageClientManager might be already gone. Change-Id: Ia674a8c76e0c1bb39e1503be9cffc20fd69120bb Reviewed-by: Reviewed-by: Marcus Tillmanns Reviewed-by: Christian Stenger --- .../languageclient/languageclientmanager.cpp | 15 ++++++++------- .../languageclient/languageclientmanager.h | 1 - 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp index 47624ca5f29..d3c2220fc88 100644 --- a/src/plugins/languageclient/languageclientmanager.cpp +++ b/src/plugins/languageclient/languageclientmanager.cpp @@ -35,6 +35,7 @@ namespace LanguageClient { static Q_LOGGING_CATEGORY(Log, "qtc.languageclient.manager", QtWarningMsg) static LanguageClientManager *managerInstance = nullptr; +static bool g_shuttingDown = false; LanguageClientManager::LanguageClientManager(QObject *parent) : QObject (parent) @@ -112,7 +113,7 @@ void LanguageClientManager::clientStarted(Client *client) qCDebug(Log) << "client started: " << client->name() << client; QTC_ASSERT(managerInstance, return); QTC_ASSERT(client, return); - if (managerInstance->m_shuttingDown) { + if (g_shuttingDown) { clientFinished(client); return; } @@ -131,7 +132,7 @@ void LanguageClientManager::clientFinished(Client *client) && client->state() != Client::ShutdownRequested; if (unexpectedFinish) { - if (!managerInstance->m_shuttingDown) { + if (!g_shuttingDown) { const QList &clientDocs = managerInstance->m_clientForDocument.keys(client); if (client->reset()) { @@ -153,7 +154,7 @@ void LanguageClientManager::clientFinished(Client *client) } } deleteClient(client); - if (managerInstance->m_shuttingDown && managerInstance->m_clients.isEmpty()) + if (g_shuttingDown && managerInstance->m_clients.isEmpty()) emit managerInstance->shutdownFinished(); } @@ -218,17 +219,17 @@ void LanguageClientManager::deleteClient(Client *client) for (QList &clients : managerInstance->m_clientsForSetting) clients.removeAll(client); client->deleteLater(); - if (!managerInstance->m_shuttingDown) + if (!g_shuttingDown) emit instance()->clientRemoved(client); } void LanguageClientManager::shutdown() { QTC_ASSERT(managerInstance, return); - if (managerInstance->m_shuttingDown) + if (g_shuttingDown) return; qCDebug(Log) << "shutdown manager"; - managerInstance->m_shuttingDown = true; + g_shuttingDown = true; const auto clients = managerInstance->clients(); for (Client *client : clients) shutdownClient(client); @@ -242,7 +243,7 @@ void LanguageClientManager::shutdown() bool LanguageClientManager::isShuttingDown() { - return managerInstance->m_shuttingDown; + return g_shuttingDown; } LanguageClientManager *LanguageClientManager::instance() diff --git a/src/plugins/languageclient/languageclientmanager.h b/src/plugins/languageclient/languageclientmanager.h index 9e3aa0b30c1..7ce055f1fb2 100644 --- a/src/plugins/languageclient/languageclientmanager.h +++ b/src/plugins/languageclient/languageclientmanager.h @@ -100,7 +100,6 @@ private: QList reachableClients(); - bool m_shuttingDown = false; QList m_clients; QList m_currentSettings; // owned QMap> m_clientsForSetting; From 39441654d8dab11a83d4c06586baeb396c782498 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 12 Oct 2022 13:41:17 +0200 Subject: [PATCH 057/143] Squish: Correct handling of global scripts Global scripts get registered to the server. If we have valid squish settings we are able to automatically re-open them. Change-Id: Iaeed2629dac30b786b6e8faf371bc6987a4a5681 Reviewed-by: David Schulz --- src/plugins/squish/squishfilehandler.cpp | 18 +++++++++++++ src/plugins/squish/squishfilehandler.h | 2 ++ src/plugins/squish/squishplugin.cpp | 31 +++++++++++++++++++++- src/plugins/squish/squishplugin.h | 1 + src/plugins/squish/squishtesttreemodel.cpp | 3 +++ src/plugins/squish/squishtools.cpp | 27 +++++++++++++++++-- src/plugins/squish/squishtools.h | 5 ++++ 7 files changed, 84 insertions(+), 3 deletions(-) diff --git a/src/plugins/squish/squishfilehandler.cpp b/src/plugins/squish/squishfilehandler.cpp index 115086bc742..ec267106e5b 100644 --- a/src/plugins/squish/squishfilehandler.cpp +++ b/src/plugins/squish/squishfilehandler.cpp @@ -444,6 +444,24 @@ void SquishFileHandler::addSharedFolder() emit testTreeItemCreated(item); } +void SquishFileHandler::setSharedFolders(const Utils::FilePaths &folders) +{ + emit clearedSharedFolders(); + m_sharedFolders.clear(); + + for (const Utils::FilePath &folder : folders) { + if (m_sharedFolders.contains(folder)) + continue; + + m_sharedFolders.append(folder); + SquishTestTreeItem *item = new SquishTestTreeItem(folder.toUserOutput(), + SquishTestTreeItem::SquishSharedFolder); + item->setFilePath(folder); + addAllEntriesRecursively(item); + emit testTreeItemCreated(item); + } +} + bool SquishFileHandler::removeSharedFolder(const Utils::FilePath &folder) { if (m_sharedFolders.contains(folder)) diff --git a/src/plugins/squish/squishfilehandler.h b/src/plugins/squish/squishfilehandler.h index 0a23f918efc..b7ceeebc1ea 100644 --- a/src/plugins/squish/squishfilehandler.h +++ b/src/plugins/squish/squishfilehandler.h @@ -29,11 +29,13 @@ public: void runTestSuite(const QString &suiteName); void recordTestCase(const QString &suiteName, const QString &testCaseName); void addSharedFolder(); + void setSharedFolders(const Utils::FilePaths &folders); bool removeSharedFolder(const Utils::FilePath &folder); void removeAllSharedFolders(); void openObjectsMap(const QString &suiteName); signals: + void clearedSharedFolders(); void testTreeItemCreated(SquishTestTreeItem *item); void suiteTreeItemRemoved(const QString &suiteName); void suiteTreeItemModified(SquishTestTreeItem *item, const QString &displayName); diff --git a/src/plugins/squish/squishplugin.cpp b/src/plugins/squish/squishplugin.cpp index ced619fbca5..ce9fdffbc6c 100644 --- a/src/plugins/squish/squishplugin.cpp +++ b/src/plugins/squish/squishplugin.cpp @@ -4,7 +4,7 @@ #include "squishplugin.h" #include "objectsmapeditor.h" -#include "squish/squishwizardpages.h" +#include "squishfilehandler.h" #include "squishnavigationwidget.h" #include "squishoutputpane.h" #include "squishresultmodel.h" @@ -12,6 +12,7 @@ #include "squishtesttreemodel.h" #include "squishtools.h" #include "squishtr.h" +#include "squishwizardpages.h" #include #include @@ -21,6 +22,7 @@ #include +#include #include #include @@ -39,6 +41,7 @@ public: ~SquishPluginPrivate(); void initializeMenuEntries(); + bool initializeGlobalScripts(); SquishSettings m_squishSettings; SquishSettingsPage m_settingsPage{&m_squishSettings}; @@ -102,6 +105,27 @@ void SquishPluginPrivate::initializeMenuEntries() toolsMenu->addMenu(menu); } +bool SquishPluginPrivate::initializeGlobalScripts() +{ + QTC_ASSERT(dd->m_squishTools, return false); + + const Utils::FilePath squishserver = dd->m_squishSettings.squishPath.filePath().pathAppended( + Utils::HostOsInfo::withExecutableSuffix("bin/squishserver")); + if (!squishserver.isExecutableFile()) + return false; + + dd->m_squishTools->queryGlobalScripts([](const QString &output, const QString &error) { + if (output.isEmpty() || !error.isEmpty()) + return; // ignore (for now?) + + // FIXME? comma, special characters in paths + const Utils::FilePaths globalDirs = Utils::transform( + output.trimmed().split(',', Qt::SkipEmptyParts), &Utils::FilePath::fromString); + SquishFileHandler::instance()->setSharedFolders(globalDirs); + }); + return true; +} + bool SquishPlugin::initialize(const QStringList &, QString *) { dd = new SquishPluginPrivate; @@ -109,6 +133,11 @@ bool SquishPlugin::initialize(const QStringList &, QString *) return true; } +bool SquishPlugin::delayedInitialize() +{ + return dd->initializeGlobalScripts(); +} + ExtensionSystem::IPlugin::ShutdownFlag SquishPlugin::aboutToShutdown() { if (dd->m_squishTools) { diff --git a/src/plugins/squish/squishplugin.h b/src/plugins/squish/squishplugin.h index 44e49c2ab43..3373bbf2d62 100644 --- a/src/plugins/squish/squishplugin.h +++ b/src/plugins/squish/squishplugin.h @@ -23,6 +23,7 @@ public: static SquishSettings *squishSettings(); bool initialize(const QStringList &arguments, QString *errorString) override; + bool delayedInitialize() override; ShutdownFlag aboutToShutdown() override; }; diff --git a/src/plugins/squish/squishtesttreemodel.cpp b/src/plugins/squish/squishtesttreemodel.cpp index 8df29777363..3733d10f8fb 100644 --- a/src/plugins/squish/squishtesttreemodel.cpp +++ b/src/plugins/squish/squishtesttreemodel.cpp @@ -186,6 +186,9 @@ SquishTestTreeModel::SquishTestTreeModel(QObject *parent) &SquishFileHandler::suiteTreeItemRemoved, this, &SquishTestTreeModel::onSuiteTreeItemRemoved); + connect(m_squishFileHandler, + &SquishFileHandler::clearedSharedFolders, + this, [this]() { m_squishSharedFolders->removeChildren(); }); m_instance = this; } diff --git a/src/plugins/squish/squishtools.cpp b/src/plugins/squish/squishtools.cpp index df63485fcc0..0cfb16d1c84 100644 --- a/src/plugins/squish/squishtools.cpp +++ b/src/plugins/squish/squishtools.cpp @@ -260,11 +260,22 @@ void SquishTools::runTestCases(const FilePath &suitePath, startSquishServer(RunTestRequested); } +void SquishTools::queryGlobalScripts(QueryCallback callback) +{ + m_queryCallback = callback; + queryServer(GetGlobalScriptDirs); +} + void SquishTools::queryServerSettings(QueryCallback callback) +{ + m_queryCallback = callback; + queryServer(ServerInfo); +} + +void SquishTools::queryServer(RunnerQuery query) { if (m_shutdownInitiated) return; - m_queryCallback = callback; if (m_state != Idle) { QMessageBox::critical(Core::ICore::dialogParent(), @@ -276,6 +287,7 @@ void SquishTools::queryServerSettings(QueryCallback callback) } m_perspective.setPerspectiveMode(SquishPerspective::Querying); m_fullRunnerOutput.clear(); + m_query = query; startSquishServer(RunnerQueryRequested); } @@ -663,7 +675,18 @@ void SquishTools::executeRunnerQuery() if (!isValidToStartRunner() || !setupRunnerPath()) return; - setupAndStartSquishRunnerProcess({ "--port", QString::number(m_serverPort), "--info", "all"}); + QStringList arguments = { "--port", QString::number(m_serverPort) }; + switch (m_query) { + case ServerInfo: + arguments << "--info" << "all"; + break; + case GetGlobalScriptDirs: + arguments << "--config" << "getGlobalScriptDirs"; + break; + default: + QTC_ASSERT(false, return); + } + setupAndStartSquishRunnerProcess(arguments); } Environment SquishTools::squishEnvironment() diff --git a/src/plugins/squish/squishtools.h b/src/plugins/squish/squishtools.h index d9400f7ad07..8992a8a8990 100644 --- a/src/plugins/squish/squishtools.h +++ b/src/plugins/squish/squishtools.h @@ -68,6 +68,7 @@ public: const QStringList &testCases = QStringList()); void recordTestCase(const Utils::FilePath &suitePath, const QString &testCaseName, const SuiteConf &suiteConf); + void queryGlobalScripts(QueryCallback callback); void queryServerSettings(QueryCallback callback); void writeServerSettingsChanges(const QList &changes); void requestExpansion(const QString &name); @@ -98,6 +99,8 @@ private: KillOldBeforeQueryRunner }; + enum RunnerQuery { ServerInfo, GetGlobalScriptDirs }; + void setState(State state); void handleSetStateStartAppRunner(); void handleSetStateQueryRunner(); @@ -107,6 +110,7 @@ private: void startSquishRunner(); void setupAndStartRecorder(); void stopRecorder(); + void queryServer(RunnerQuery query); void executeRunnerQuery(); static Utils::Environment squishEnvironment(); void onServerFinished(); @@ -163,6 +167,7 @@ private: qint64 m_readResultsCount; int m_autId = 0; QueryCallback m_queryCallback; + RunnerQuery m_query = ServerInfo; bool m_shutdownInitiated = false; bool m_closeRunnerOnEndRecord = false; bool m_licenseIssues = false; From 8a9072b8c27f53636f3767514af41a4d3b82213e Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 12 Oct 2022 15:08:03 +0200 Subject: [PATCH 058/143] Squish: Handle changing configured Squish path Update shared folders if the path to Squish has changed. Change-Id: Iea861d1f51dd4170bc4b481176fa1a8019c24d3d Reviewed-by: David Schulz --- src/plugins/squish/squishplugin.cpp | 5 +++++ src/plugins/squish/squishsettings.cpp | 2 ++ src/plugins/squish/squishsettings.h | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/src/plugins/squish/squishplugin.cpp b/src/plugins/squish/squishplugin.cpp index ce9fdffbc6c..f2e486770c9 100644 --- a/src/plugins/squish/squishplugin.cpp +++ b/src/plugins/squish/squishplugin.cpp @@ -108,6 +108,7 @@ void SquishPluginPrivate::initializeMenuEntries() bool SquishPluginPrivate::initializeGlobalScripts() { QTC_ASSERT(dd->m_squishTools, return false); + SquishFileHandler::instance()->setSharedFolders({}); const Utils::FilePath squishserver = dd->m_squishSettings.squishPath.filePath().pathAppended( Utils::HostOsInfo::withExecutableSuffix("bin/squishserver")); @@ -135,6 +136,10 @@ bool SquishPlugin::initialize(const QStringList &, QString *) bool SquishPlugin::delayedInitialize() { + + connect(&dd->m_squishSettings, &SquishSettings::squishPathChanged, + dd, &SquishPluginPrivate::initializeGlobalScripts); + return dd->initializeGlobalScripts(); } diff --git a/src/plugins/squish/squishsettings.cpp b/src/plugins/squish/squishsettings.cpp index 17daef73f41..58fad483e77 100644 --- a/src/plugins/squish/squishsettings.cpp +++ b/src/plugins/squish/squishsettings.cpp @@ -84,6 +84,8 @@ SquishSettings::SquishSettings() serverHost.setEnabled(!checked); serverPort.setEnabled(!checked); }); + connect(&squishPath, &Utils::StringAspect::valueChanged, + this, &SquishSettings::squishPathChanged); } Utils::FilePath SquishSettings::scriptsPath(Language language) const diff --git a/src/plugins/squish/squishsettings.h b/src/plugins/squish/squishsettings.h index 1f411cde60a..02b84d94937 100644 --- a/src/plugins/squish/squishsettings.h +++ b/src/plugins/squish/squishsettings.h @@ -38,6 +38,7 @@ public: class SquishSettings : public Utils::AspectContainer { + Q_OBJECT public: SquishSettings(); @@ -50,6 +51,9 @@ public: Utils::BoolAspect local; Utils::BoolAspect verbose; Utils::BoolAspect minimizeIDE; + +signals: + void squishPathChanged(); }; class SquishSettingsPage final : public Core::IOptionsPage From 9335f67307168f7a3275f90b8bfe769b73ddd4d9 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Thu, 13 Oct 2022 14:50:34 +0200 Subject: [PATCH 059/143] Fix that Open Documents no longer showed "pinned" icon Or "locked" icon. Amends 373b9f8b310254c526ca12a003e066d085655e5d Change-Id: Ieaf10ade422dce65700c3b4360b496a1cec70160 Reviewed-by: Cristian Adam Reviewed-by: Qt CI Bot Reviewed-by: --- src/plugins/coreplugin/editormanager/openeditorsview.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/coreplugin/editormanager/openeditorsview.cpp b/src/plugins/coreplugin/editormanager/openeditorsview.cpp index 87e4174d2fd..f87d40000dd 100644 --- a/src/plugins/coreplugin/editormanager/openeditorsview.cpp +++ b/src/plugins/coreplugin/editormanager/openeditorsview.cpp @@ -205,6 +205,9 @@ void ProxyModel::setSourceModel(QAbstractItemModel *sm) QVariant ProxyModel::data(const QModelIndex &index, int role) const { if (role == Qt::DecorationRole && index.column() == 0) { + const QVariant sourceDecoration = QAbstractProxyModel::data(index, role); + if (sourceDecoration.isValid()) + return sourceDecoration; const QString fileName = QAbstractProxyModel::data(index, Qt::DisplayRole).toString(); return Utils::FileIconProvider::icon(Utils::FilePath::fromString(fileName)); } From 6fc313013e12507e20f1a9d4951c22097655dc78 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Thu, 13 Oct 2022 18:54:05 +0200 Subject: [PATCH 060/143] ProjectExplorer: Skip clink autorun for msvc detection Clink checks the CLINK_NOAUTORUN environment variable, and if it's set it does a quick exit and the msvc detection is faster. See https://github.com/chrisant996/clink/issues/361 Task-number: QTCREATORBUG-27906 Change-Id: Ib2cbc29a6135e5a1eb7411e4353cc1df8ef59004 Reviewed-by: Reviewed-by: David Schulz --- src/plugins/projectexplorer/msvctoolchain.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index d86a0ce116c..b320e474f41 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -2094,6 +2094,7 @@ std::optional MsvcToolChain::generateEnvironmentSettings(const Utils::E if (Utils::HostOsInfo::isWindowsHost()) saver.write("chcp 65001\r\n"); saver.write("set VSCMD_SKIP_SENDTELEMETRY=1\r\n"); + saver.write("set CLINK_NOAUTORUN=1\r\n"); saver.write(call + "\r\n"); saver.write("@echo " + marker.toLocal8Bit() + "\r\n"); saver.write("set\r\n"); From 45ec26783f484a3138de6be6c4e36e658b3f94fd Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Thu, 13 Oct 2022 15:45:40 +0200 Subject: [PATCH 061/143] CMakePM: Search after ninja also in system environment CMakePM was setting the default generator to Ninja if the ninja executable was set in the build environment. This had the side effect of having to instantiate the build environment for Visual C++, which meant running vcvars.bat files. This operation can be costly if clink is also installed in the system. This way the users that have ninja installed via choco, or manually set it path, will not be affected by this delay. Note that this only fixes the issue for the first start of Qt Creator with new settings. Some number from my machine: - with clink 21s - without clink 10s - with this patchset 4s Task-number: QTCREATORBUG-27906 Change-Id: I74d19b08211d93b3962a8877b49a58089310fbd6 Reviewed-by: Cristian Adam --- src/plugins/cmakeprojectmanager/cmakekitinformation.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp index 42eccdf7e06..523bafca68d 100644 --- a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp +++ b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp @@ -682,8 +682,11 @@ QVariant CMakeGeneratorKitAspect::defaultValue(const Kit *k) const = Internal::CMakeProjectPlugin::projectTypeSpecificSettings(); if (settings->ninjaPath.filePath().isEmpty()) { - Environment env = k->buildEnvironment(); - return !env.searchInPath("ninja").isEmpty(); + auto findNinja = [](const Environment &env) -> bool { + return !env.searchInPath("ninja").isEmpty(); + }; + if (!findNinja(Environment::systemEnvironment())) + return findNinja(k->buildEnvironment()); } return true; }(); From 2fd4be46034c066f941c90b4f01a9134c3e25489 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 14 Oct 2022 09:54:39 +0200 Subject: [PATCH 062/143] ProjectExplorer: Use FilePath in gcc toolchain Change-Id: I2cd659dc94e0c9ee1a5f198098a3886146c3e03b Reviewed-by: hjk --- src/plugins/projectexplorer/gcctoolchain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index 884412f5b5f..bdce4800105 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -1196,8 +1196,8 @@ Toolchains GccToolChainFactory::autoDetectToolchains( && HostOsInfo::isWindowsHost() && !existingCommand.needsDevice() && !compilerPath.needsDevice()) { - existingTcMatches = existingCommand.toFileInfo().size() - == compilerPath.toFileInfo().size(); + existingTcMatches = existingCommand.fileSize() + == compilerPath.fileSize(); } } if (existingTcMatches) { From 296538d23d4618c54f60e59382f8f3308ff263c6 Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 14 Oct 2022 10:11:13 +0200 Subject: [PATCH 063/143] Utils: Add FilePath::isSymLink Needed to fix GenericUploadStep handle the device->device case. Change-Id: I551ac0957879e5b8737c22fa823d8b2e6bec0b5e Reviewed-by: Marcus Tillmanns Reviewed-by: hjk --- src/libs/utils/devicefileaccess.cpp | 19 +++++++++++++++++++ src/libs/utils/devicefileaccess.h | 3 +++ src/libs/utils/filepath.cpp | 5 +++++ src/libs/utils/filepath.h | 1 + 4 files changed, 28 insertions(+) diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp index 63fc456c8ed..670570e86af 100644 --- a/src/libs/utils/devicefileaccess.cpp +++ b/src/libs/utils/devicefileaccess.cpp @@ -82,6 +82,13 @@ bool DeviceFileAccess::isDirectory(const FilePath &filePath) const return false; } +bool DeviceFileAccess::isSymLink(const FilePath &filePath) const +{ + Q_UNUSED(filePath) + QTC_CHECK(false); + return false; +} + bool DeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const { if (isWritableDirectory(filePath)) @@ -317,6 +324,12 @@ bool DesktopDeviceFileAccess::isDirectory(const FilePath &filePath) const return fi.isDir(); } +bool DesktopDeviceFileAccess::isSymLink(const FilePath &filePath) const +{ + const QFileInfo fi(filePath.path()); + return fi.isSymLink(); +} + bool DesktopDeviceFileAccess::ensureWritableDirectory(const FilePath &filePath) const { const QFileInfo fi(filePath.path()); @@ -656,6 +669,12 @@ bool UnixDeviceFileAccess::isDirectory(const FilePath &filePath) const return runInShellSuccess("test", {"-d", path}); } +bool UnixDeviceFileAccess::isSymLink(const FilePath &filePath) const +{ + const QString path = filePath.path(); + return runInShellSuccess("test", {"-h", path}); +} + bool UnixDeviceFileAccess::ensureExistingFile(const FilePath &filePath) const { const QString path = filePath.path(); diff --git a/src/libs/utils/devicefileaccess.h b/src/libs/utils/devicefileaccess.h index f638d162606..90c3f70f958 100644 --- a/src/libs/utils/devicefileaccess.h +++ b/src/libs/utils/devicefileaccess.h @@ -27,6 +27,7 @@ protected: virtual bool isWritableDirectory(const FilePath &filePath) const; virtual bool isFile(const FilePath &filePath) const; virtual bool isDirectory(const FilePath &filePath) const; + virtual bool isSymLink(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; @@ -93,6 +94,7 @@ protected: bool isWritableDirectory(const FilePath &filePath) const override; bool isFile(const FilePath &filePath) const override; bool isDirectory(const FilePath &filePath) const override; + bool isSymLink(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; @@ -149,6 +151,7 @@ protected: bool isWritableDirectory(const FilePath &filePath) const override; bool isFile(const FilePath &filePath) const override; bool isDirectory(const FilePath &filePath) const override; + bool isSymLink(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; diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 057ed05847b..f5e86d922d0 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -407,6 +407,11 @@ bool FilePath::isDir() const return fileAccess()->isDirectory(*this); } +bool FilePath::isSymLink() const +{ + return fileAccess()->isSymLink(*this); +} + bool FilePath::createDir() const { return fileAccess()->createDirectory(*this); diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h index 97e5752ccf8..9bb65be2935 100644 --- a/src/libs/utils/filepath.h +++ b/src/libs/utils/filepath.h @@ -107,6 +107,7 @@ public: bool isAbsolutePath() const { return !isRelativePath(); } bool isFile() const; bool isDir() const; + bool isSymLink() const; bool isRootPath() const; bool isNewerThan(const QDateTime &timeStamp) const; QDateTime lastModified() const; From 519b6fefe10629c1a09df03b59647c0f1085a806 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Thu, 13 Oct 2022 11:56:12 +0200 Subject: [PATCH 064/143] Link with Qt: Prepare for sdktool to move to a different location When linking we should now consider the new sdktool location within the Qt installation first. The new location of QtCreator.ini will be / Tools/sdktool/QtProject/QtCreator.ini on macOS, and /Tools/sdktool/ share/qtcreator/QtProject/QtCreator.ini on Windows/Linux. When resolving an existing install settings path to the old location within the Qt Creator installation (Qt Creator.app/Contents/Resources or Tools/QtCreator/share/qtcreator), the QtCreator.ini at that location will itself redirect to the new sdktool location. So, try to follow that as well, to keep existing setups working. Task-number: QTBUG-28101 Change-Id: Idbb09ad6961f6fd4a4112830959a615d8186a132 Reviewed-by: David Schulz Reviewed-by: Qt CI Bot --- src/app/main.cpp | 37 +++++++++++++++++-------- src/plugins/qtsupport/qtoptionspage.cpp | 4 +-- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/app/main.cpp b/src/app/main.cpp index ee7ddbd2035..d56cc676020 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -244,22 +244,35 @@ static void setupInstallSettings(QString &installSettingspath) QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR), QLatin1String(Core::Constants::IDE_CASED_ID))); installSettingspath.clear(); } - // Check if the default install settings contain a setting for the actual install settings. - // This can be an absolute path, or a path relative to applicationDirPath(). - // The result is interpreted like -settingspath, but for SystemScope static const char kInstallSettingsKey[] = "Settings/InstallSettings"; QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope, installSettingspath.isEmpty() ? resourcePath() : installSettingspath); - QSettings installSettings(QSettings::IniFormat, QSettings::UserScope, - QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR), - QLatin1String(Core::Constants::IDE_CASED_ID)); - if (installSettings.contains(kInstallSettingsKey)) { - QString installSettingsPath = installSettings.value(kInstallSettingsKey).toString(); - if (QDir::isRelativePath(installSettingsPath)) - installSettingsPath = applicationDirPath() + '/' + installSettingsPath; - QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope, installSettingsPath); - } + // Check if the default install settings contain a setting for the actual install settings. + // This can be an absolute path, or a path relative to applicationDirPath(). + // The result is interpreted like -settingspath, but for SystemScope. + // + // Through the sdktool split that is upcoming, the new install settings might redirect + // yet a second time. So try this a few times. + // (Only the first time with QSettings::UserScope, to allow setting the install settings path + // in the user settings.) + QSettings::Scope scope = QSettings::UserScope; + int count = 0; + bool containsInstallSettingsKey = false; + do { + QSettings installSettings(QSettings::IniFormat, scope, + QLatin1String(Core::Constants::IDE_SETTINGSVARIANT_STR), + QLatin1String(Core::Constants::IDE_CASED_ID)); + containsInstallSettingsKey = installSettings.contains(kInstallSettingsKey); + if (containsInstallSettingsKey) { + QString newInstallSettingsPath = installSettings.value(kInstallSettingsKey).toString(); + if (QDir::isRelativePath(newInstallSettingsPath)) + newInstallSettingsPath = applicationDirPath() + '/' + newInstallSettingsPath; + QSettings::setPath(QSettings::IniFormat, QSettings::SystemScope, newInstallSettingsPath); + } + scope = QSettings::SystemScope; // UserScope only the first time we check + ++count; + } while (containsInstallSettingsKey && count < 3); } static Utils::QtcSettings *createUserSettings() diff --git a/src/plugins/qtsupport/qtoptionspage.cpp b/src/plugins/qtsupport/qtoptionspage.cpp index c0049058abd..9df0dd5068f 100644 --- a/src/plugins/qtsupport/qtoptionspage.cpp +++ b/src/plugins/qtsupport/qtoptionspage.cpp @@ -897,9 +897,9 @@ void QtOptionsPageWidget::apply() &QtOptionsPageWidget::updateQtVersions); } -// TODO whenever we move the output of sdktool to a different location in the installer, -// this needs to be adapted accordingly const QStringList kSubdirsToCheck = {"", + "Tools/sdktool", // macOS + "Tools/sdktool/share/qtcreator", // Windows/Linux "Qt Creator.app/Contents/Resources", "Contents/Resources", "Tools/QtCreator/share/qtcreator", From f079a36200f312ea95e9d84b510b366a079cf225 Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Thu, 13 Oct 2022 12:04:12 +0200 Subject: [PATCH 065/143] Utils: Re-increase font size for DetailsButton on macOS Fixes: QTCREATORBUG-28266 Change-Id: Iab9ef85cb30e5c75f20bda34f7ceee7a0363aa1c Reviewed-by: Eike Ziller --- src/libs/utils/detailsbutton.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/utils/detailsbutton.cpp b/src/libs/utils/detailsbutton.cpp index 8c20ea62f68..d5ab48238f8 100644 --- a/src/libs/utils/detailsbutton.cpp +++ b/src/libs/utils/detailsbutton.cpp @@ -7,9 +7,10 @@ #include #include -#include +#include #include #include +#include #include #include @@ -69,6 +70,8 @@ DetailsButton::DetailsButton(QWidget *parent) { setText(tr("Details")); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); + if (HostOsInfo::isMacHost()) + setFont(QGuiApplication::font()); } QSize DetailsButton::sizeHint() const From 1b6728538ec1325ff77aacfb24bb1c2cf1e50cb1 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 14 Oct 2022 09:45:31 +0200 Subject: [PATCH 066/143] Clang: Fix compile_commands path setup Change-Id: I2af0935ad022714f8e929c483455a8800e9f7cca Reviewed-by: Christian Kandeler --- src/plugins/clangcodemodel/clangutils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp index 47718511881..a50d7ec9b31 100644 --- a/src/plugins/clangcodemodel/clangutils.cpp +++ b/src/plugins/clangcodemodel/clangutils.cpp @@ -155,7 +155,7 @@ GenerateCompilationDbResult generateCompilationDB(QList p QTC_ASSERT(!projectInfoList.isEmpty(), return GenerateCompilationDbResult(QString(), "Could not retrieve project info.")); QTC_CHECK(baseDir.ensureWritableDir()); - QFile compileCommandsFile(baseDir.toString() + "/compile_commands.json"); + QFile compileCommandsFile(baseDir.pathAppended("compile_commands.json").toFSPathString()); const bool fileOpened = compileCommandsFile.open(QIODevice::WriteOnly | QIODevice::Truncate); if (!fileOpened) { return GenerateCompilationDbResult(QString(), From 01b07c0563c4f3f9465ef54bd7af2f766c1212f4 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Tue, 11 Oct 2022 13:10:10 +0200 Subject: [PATCH 067/143] LanguageClient: deactivate documents on client shutdown Change-Id: I0a3aa89b1390f3cb7d5816bb85d9fd538fb3f90b Reviewed-by: Christian Stenger --- src/plugins/languageclient/client.cpp | 1 + src/plugins/languageclient/languageclientmanager.cpp | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index be68f933639..d61f3c720a7 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -879,6 +879,7 @@ void Client::deactivateDocument(TextEditor::TextDocument *document) TextEditor::TextEditorWidget *widget = textEditor->editorWidget(); widget->removeHoverHandler(&d->m_hoverHandler); widget->setExtraSelections(TextEditor::TextEditorWidget::CodeSemanticsSelection, {}); + updateEditorToolBar(editor); } } } diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp index d3c2220fc88..d4f5c442642 100644 --- a/src/plugins/languageclient/languageclientmanager.cpp +++ b/src/plugins/languageclient/languageclientmanager.cpp @@ -199,10 +199,10 @@ void LanguageClientManager::shutdownClient(Client *client) if (!client) return; qCDebug(Log) << "request client shutdown: " << client->name() << client; - // reset the documents for that client already when requesting the shutdown so they can get - // reassigned to another server right after this request to another server + // reset and deactivate the documents for that client by assigning a null client already when + // requesting the shutdown so they can get reassigned to another server right after this request for (TextEditor::TextDocument *document : managerInstance->m_clientForDocument.keys(client)) - managerInstance->m_clientForDocument.remove(document); + openDocumentWithClient(document, nullptr); if (client->reachable()) client->shutdown(); else if (client->state() != Client::Shutdown && client->state() != Client::ShutdownRequested) @@ -409,6 +409,7 @@ void LanguageClientManager::openDocumentWithClient(TextEditor::TextDocument *doc Client *currentClient = clientForDocument(document); if (client == currentClient) return; + managerInstance->m_clientForDocument.remove(document); if (currentClient) currentClient->deactivateDocument(document); managerInstance->m_clientForDocument[document] = client; From 9e4eabb410ce8aa9e3975a4f47b5fd64fdd5dea5 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 13 Oct 2022 14:35:25 +0200 Subject: [PATCH 068/143] RemoteLinux: Dissolve AbstractRemoteLinuxDeployStep::createDeployService The previous setup doesn't buy much in comparison to new + setDeployService() combo. Making that explicit now opens the path to simplify the two-phase creation (i.e. move setInternalInitializer to the service c'tor). Change-Id: Ib66c7d02efcddd6909fe612a786034e2728cdedf Reviewed-by: Christian Kandeler Reviewed-by: Qt CI Bot --- src/plugins/boot2qt/qdbmakedefaultappstep.cpp | 3 ++- src/plugins/boot2qt/qdbstopapplicationstep.cpp | 3 ++- .../remotelinux/abstractremotelinuxdeploystep.h | 10 +--------- src/plugins/remotelinux/customcommanddeploystep.cpp | 3 ++- src/plugins/remotelinux/genericdirectuploadstep.cpp | 3 ++- src/plugins/remotelinux/killappstep.cpp | 3 ++- src/plugins/remotelinux/rsyncdeploystep.cpp | 3 ++- src/plugins/remotelinux/tarpackagedeploystep.cpp | 3 ++- 8 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/plugins/boot2qt/qdbmakedefaultappstep.cpp b/src/plugins/boot2qt/qdbmakedefaultappstep.cpp index 8b89c3ed22b..3c5f0e3814d 100644 --- a/src/plugins/boot2qt/qdbmakedefaultappstep.cpp +++ b/src/plugins/boot2qt/qdbmakedefaultappstep.cpp @@ -92,7 +92,8 @@ public: QdbMakeDefaultAppStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id) { - auto service = createDeployService(); + auto service = new QdbMakeDefaultAppService; + setDeployService(service); auto selection = addAspect(); selection->setSettingsKey("QdbMakeDefaultDeployStep.MakeDefault"); diff --git a/src/plugins/boot2qt/qdbstopapplicationstep.cpp b/src/plugins/boot2qt/qdbstopapplicationstep.cpp index 9899598d3e1..e3b3f1b2c7e 100644 --- a/src/plugins/boot2qt/qdbstopapplicationstep.cpp +++ b/src/plugins/boot2qt/qdbstopapplicationstep.cpp @@ -115,7 +115,8 @@ public: QdbStopApplicationStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id) { - auto service = createDeployService(); + auto service = new QdbStopApplicationService; + setDeployService(service); setWidgetExpandedByDefault(false); diff --git a/src/plugins/remotelinux/abstractremotelinuxdeploystep.h b/src/plugins/remotelinux/abstractremotelinuxdeploystep.h index 69c1983994a..4946a0849bb 100644 --- a/src/plugins/remotelinux/abstractremotelinuxdeploystep.h +++ b/src/plugins/remotelinux/abstractremotelinuxdeploystep.h @@ -32,17 +32,9 @@ protected: void setInternalInitializer(const std::function &init); void setRunPreparer(const std::function &prep); - - template - T *createDeployService() - { - T *service = new T; - setDeployService(service); - return service; - } + void setDeployService(AbstractRemoteLinuxDeployService *service); private: - void setDeployService(AbstractRemoteLinuxDeployService *service); void handleProgressMessage(const QString &message); void handleErrorMessage(const QString &message); void handleWarningMessage(const QString &message); diff --git a/src/plugins/remotelinux/customcommanddeploystep.cpp b/src/plugins/remotelinux/customcommanddeploystep.cpp index 4bc5a715283..8a7104c42df 100644 --- a/src/plugins/remotelinux/customcommanddeploystep.cpp +++ b/src/plugins/remotelinux/customcommanddeploystep.cpp @@ -92,7 +92,8 @@ public: CustomCommandDeployStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id) { - auto service = createDeployService(); + auto service = new CustomCommandDeployService; + setDeployService(service); auto commandLine = addAspect(); commandLine->setSettingsKey("RemoteLinuxCustomCommandDeploymentStep.CommandLine"); diff --git a/src/plugins/remotelinux/genericdirectuploadstep.cpp b/src/plugins/remotelinux/genericdirectuploadstep.cpp index 85383f1e213..c01b21c3384 100644 --- a/src/plugins/remotelinux/genericdirectuploadstep.cpp +++ b/src/plugins/remotelinux/genericdirectuploadstep.cpp @@ -20,7 +20,8 @@ GenericDirectUploadStep::GenericDirectUploadStep(BuildStepList *bsl, Utils::Id i bool offerIncrementalDeployment) : AbstractRemoteLinuxDeployStep(bsl, id) { - auto service = createDeployService(); + auto service = new GenericDirectUploadService; + setDeployService(service); BoolAspect *incremental = nullptr; if (offerIncrementalDeployment) { diff --git a/src/plugins/remotelinux/killappstep.cpp b/src/plugins/remotelinux/killappstep.cpp index af19055d509..09670155961 100644 --- a/src/plugins/remotelinux/killappstep.cpp +++ b/src/plugins/remotelinux/killappstep.cpp @@ -106,7 +106,8 @@ class KillAppStep : public AbstractRemoteLinuxDeployStep public: KillAppStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id) { - auto service = createDeployService(); + auto service = new Internal::KillAppService; + setDeployService(service); setWidgetExpandedByDefault(false); diff --git a/src/plugins/remotelinux/rsyncdeploystep.cpp b/src/plugins/remotelinux/rsyncdeploystep.cpp index 1f6ae266282..c6cace227ef 100644 --- a/src/plugins/remotelinux/rsyncdeploystep.cpp +++ b/src/plugins/remotelinux/rsyncdeploystep.cpp @@ -139,7 +139,8 @@ void RsyncDeployService::setFinished() RsyncDeployStep::RsyncDeployStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id) { - auto service = createDeployService(); + auto service = new RsyncDeployService; + setDeployService(service); auto flags = addAspect(); flags->setDisplayStyle(StringAspect::LineEditDisplay); diff --git a/src/plugins/remotelinux/tarpackagedeploystep.cpp b/src/plugins/remotelinux/tarpackagedeploystep.cpp index 0d7d8f729c7..5dd3def96fb 100644 --- a/src/plugins/remotelinux/tarpackagedeploystep.cpp +++ b/src/plugins/remotelinux/tarpackagedeploystep.cpp @@ -192,7 +192,8 @@ public: TarPackageDeployStep(BuildStepList *bsl, Id id) : AbstractRemoteLinuxDeployStep(bsl, id) { - auto service = createDeployService(); + auto service = new TarPackageDeployService; + setDeployService(service); setWidgetExpandedByDefault(false); From c51ca1fd821ce8655710b4a9b69669a4ebcb839d Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Thu, 13 Oct 2022 10:23:59 +0200 Subject: [PATCH 069/143] Doc: Improve quality of Squish plugin icons And add the button name to text to help users find it in UI. Change-Id: I3d7096ac13855b697c97f6affe146a088241259a Reviewed-by: Reviewed-by: Christian Stenger --- doc/qtcreator/images/icons/jumpto.png | Bin 399 -> 271 bytes doc/qtcreator/images/icons/objectsmap.png | Bin 140 -> 160 bytes .../src/howto/creator-only/creator-squish.qdoc | 3 ++- 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/qtcreator/images/icons/jumpto.png b/doc/qtcreator/images/icons/jumpto.png index a913b47d8c1b9ce43932f4fa9d0311ef987cb5c6..f56df24647be505bfd78b3cfa59a2a36e9731699 100644 GIT binary patch delta 254 zcmeBY?q`}HS`kykhJ_PEXxSbfdm^rZ6KmEr{}a`onK$UM#< zZzRsQmdKI;Vst00*OPhX4Qo delta 383 zcmeBY>SvxHSgpRB z8XKFMTUxq6pu4LZ0(!c8dU|?$`zK78F=NK8S+nNNo3{uA7Oz^hYR#H8>(;H?uwlcd zO`EoD+qPrJj@`R=@7c3w-@bkI2M!!Kbm-8LBS%i0IC1LKsdMMfUAS=J(xppRu3Wix z?b?kSH*Vd!b?45Vd-v`=di3bYlPAxfJ$v!u#h*Wa{{R0!!Km>i0|Uc+PZ!4!2}#z2 z2lbc(MHns=-`{yiEZT3=iKwG_T^vI=qLULCnBBz1(-mGyNvSX-wKI8!hYP%9QjCphNXgKc c!^_5StAjo9NT|tn1_lNOPgg&ebxsLQ0Nu|gS^xk5 diff --git a/doc/qtcreator/src/howto/creator-only/creator-squish.qdoc b/doc/qtcreator/src/howto/creator-only/creator-squish.qdoc index 85d7e273e78..d6aafd4469e 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-squish.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-squish.qdoc @@ -242,7 +242,8 @@ To edit the object map of a test suite, select the \inlineimage icons/objectsmap.png - button next to the test suite in \uicontrol {Test Suites}. + (\uicontrol {Object Map}) button next to the test + suite in \uicontrol {Test Suites}. \image qtcreator-squish-symbolic-names.png "Symbolic Names view" From 737d4189895ab767edc0b7be53822759bf86c276 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Fri, 14 Oct 2022 09:03:21 +0200 Subject: [PATCH 070/143] Squish: Construct CommandLine instead of arguments list ...when preparing the runner process. Gives more control on how the arguments are used. This allows to pass arguments as "raw" arguments to avoid CommandLine internal quoting. Change-Id: I1cdf8bafa53d8618db8ab4711b04404154d3b90e Reviewed-by: David Schulz --- src/plugins/squish/squishtools.cpp | 17 +++++++++++------ src/plugins/squish/squishtools.h | 2 +- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/plugins/squish/squishtools.cpp b/src/plugins/squish/squishtools.cpp index 0cfb16d1c84..3e8f664991c 100644 --- a/src/plugins/squish/squishtools.cpp +++ b/src/plugins/squish/squishtools.cpp @@ -624,7 +624,9 @@ void SquishTools::startSquishRunner() m_autId = 0; if (m_request == RecordTestRequested) m_closeRunnerOnEndRecord = true; - setupAndStartSquishRunnerProcess(args); + + Utils::CommandLine cmdLine = {toolsSettings.runnerPath, args}; + setupAndStartSquishRunnerProcess(cmdLine); } void SquishTools::setupAndStartRecorder() @@ -676,17 +678,20 @@ void SquishTools::executeRunnerQuery() return; QStringList arguments = { "--port", QString::number(m_serverPort) }; + Utils::CommandLine cmdLine = {toolsSettings.runnerPath, arguments}; switch (m_query) { case ServerInfo: - arguments << "--info" << "all"; + cmdLine.addArg("--info"); + cmdLine.addArg("all"); break; case GetGlobalScriptDirs: - arguments << "--config" << "getGlobalScriptDirs"; + cmdLine.addArg("--config"); + cmdLine.addArg("getGlobalScriptDirs"); break; default: QTC_ASSERT(false, return); } - setupAndStartSquishRunnerProcess(arguments); + setupAndStartSquishRunnerProcess(cmdLine); } Environment SquishTools::squishEnvironment() @@ -1418,9 +1423,9 @@ bool SquishTools::setupRunnerPath() return true; } -void SquishTools::setupAndStartSquishRunnerProcess(const QStringList &args) +void SquishTools::setupAndStartSquishRunnerProcess(const Utils::CommandLine &cmdLine) { - m_runnerProcess.setCommand({toolsSettings.runnerPath, args}); + m_runnerProcess.setCommand(cmdLine); m_runnerProcess.setEnvironment(squishEnvironment()); setState(RunnerStarting); diff --git a/src/plugins/squish/squishtools.h b/src/plugins/squish/squishtools.h index 8992a8a8990..22b71c01038 100644 --- a/src/plugins/squish/squishtools.h +++ b/src/plugins/squish/squishtools.h @@ -137,7 +137,7 @@ private: QStringList serverArgumentsFromSettings() const; QStringList runnerArgumentsFromSettings(); bool setupRunnerPath(); - void setupAndStartSquishRunnerProcess(const QStringList &arg); + void setupAndStartSquishRunnerProcess(const Utils::CommandLine &cmdLine); SquishPerspective m_perspective; std::unique_ptr m_xmlOutputHandler; From 2d84e8b28ee3f6c7b2969bb02685f8b56943b0d7 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 12 Oct 2022 15:26:48 +0200 Subject: [PATCH 071/143] Squish: Inform server if global scripts get configured So far, this had been just a visual representation. But, global script folders have quite some deeper meaning than just some convenience inside the IDE. So, if the user adds or removes global script folders inform the server that this has been done. As on it, fix model mapping. Change-Id: I239de61c6b49006dd2f1e0275138aaa3022d1482 Reviewed-by: David Schulz Reviewed-by: Qt CI Bot --- src/plugins/squish/squishfilehandler.cpp | 26 +++++++++++++++++-- src/plugins/squish/squishfilehandler.h | 1 + src/plugins/squish/squishnavigationwidget.cpp | 4 +-- src/plugins/squish/squishtools.cpp | 16 ++++++++++++ src/plugins/squish/squishtools.h | 4 ++- 5 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/plugins/squish/squishfilehandler.cpp b/src/plugins/squish/squishfilehandler.cpp index ec267106e5b..3ae0cd8edf2 100644 --- a/src/plugins/squish/squishfilehandler.cpp +++ b/src/plugins/squish/squishfilehandler.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include namespace Squish { @@ -437,6 +438,8 @@ void SquishFileHandler::addSharedFolder() return; m_sharedFolders.append(chosen); + updateSquishServerGlobalScripts(); + SquishTestTreeItem *item = new SquishTestTreeItem(chosen.toUserOutput(), SquishTestTreeItem::SquishSharedFolder); item->setFilePath(chosen); @@ -464,8 +467,12 @@ void SquishFileHandler::setSharedFolders(const Utils::FilePaths &folders) bool SquishFileHandler::removeSharedFolder(const Utils::FilePath &folder) { - if (m_sharedFolders.contains(folder)) - return m_sharedFolders.removeOne(folder); + if (m_sharedFolders.contains(folder)) { + if (m_sharedFolders.removeOne(folder)) { + updateSquishServerGlobalScripts(); + return true; + } + } return false; } @@ -473,6 +480,7 @@ bool SquishFileHandler::removeSharedFolder(const Utils::FilePath &folder) void SquishFileHandler::removeAllSharedFolders() { m_sharedFolders.clear(); + updateSquishServerGlobalScripts(); } void SquishFileHandler::openObjectsMap(const QString &suiteName) @@ -509,6 +517,20 @@ void SquishFileHandler::onSessionLoaded() } } +void SquishFileHandler::updateSquishServerGlobalScripts() +{ + auto squishTools = SquishTools::instance(); + if (squishTools->state() != SquishTools::Idle) { + // postpone - we can't queue this currently + QTimer::singleShot(1500, [this]() { + updateSquishServerGlobalScripts(); + }); + return; + } + + squishTools->requestSetSharedFolders(m_sharedFolders); +} + QStringList SquishFileHandler::suitePathsAsStringList() const { return Utils::transform(m_suites.values(), &Utils::FilePath::toString); diff --git a/src/plugins/squish/squishfilehandler.h b/src/plugins/squish/squishfilehandler.h index b7ceeebc1ea..960374059b9 100644 --- a/src/plugins/squish/squishfilehandler.h +++ b/src/plugins/squish/squishfilehandler.h @@ -44,6 +44,7 @@ signals: private: void closeAllInternal(); void onSessionLoaded(); + void updateSquishServerGlobalScripts(); QStringList suitePathsAsStringList() const; void modifySuiteItem(const QString &suiteName, diff --git a/src/plugins/squish/squishnavigationwidget.cpp b/src/plugins/squish/squishnavigationwidget.cpp index 21becd62323..537aad11949 100644 --- a/src/plugins/squish/squishnavigationwidget.cpp +++ b/src/plugins/squish/squishnavigationwidget.cpp @@ -282,7 +282,7 @@ void SquishNavigationWidget::onRowsRemoved(const QModelIndex &parent, int, int) void SquishNavigationWidget::onRemoveSharedFolderTriggered(int row, const QModelIndex &parent) { - const auto folder = Utils::FilePath::fromVariant(m_model->index(row, 0, parent).data(LinkRole)); + const auto folder = Utils::FilePath::fromVariant(m_sortModel->index(row, 0, parent).data(LinkRole)); QTC_ASSERT(!folder.isEmpty(), return ); if (QMessageBox::question(Core::ICore::dialogParent(), @@ -293,7 +293,7 @@ void SquishNavigationWidget::onRemoveSharedFolderTriggered(int row, const QModel return; } - const QModelIndex &realIdx = m_sortModel->mapToSource(m_model->index(row, 0, parent)); + const QModelIndex &realIdx = m_sortModel->mapToSource(m_sortModel->index(row, 0, parent)); if (SquishFileHandler::instance()->removeSharedFolder(folder)) m_model->removeTreeItem(realIdx.row(), realIdx.parent()); } diff --git a/src/plugins/squish/squishtools.cpp b/src/plugins/squish/squishtools.cpp index 3e8f664991c..4a33d45cf3a 100644 --- a/src/plugins/squish/squishtools.cpp +++ b/src/plugins/squish/squishtools.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -272,6 +273,15 @@ void SquishTools::queryServerSettings(QueryCallback callback) queryServer(ServerInfo); } +void SquishTools::requestSetSharedFolders(const Utils::FilePaths &sharedFolders) +{ + // when sharedFolders is empty we need to pass an (explicit) empty string + // otherwise a list of paths, for convenience we quote each path + m_queryParameter = '"' + Utils::transform(sharedFolders, &FilePath::toUserOutput).join("\",\"") + '"'; + queryServer(SetGlobalScriptDirs); +} + + void SquishTools::queryServer(RunnerQuery query) { if (m_shutdownInitiated) @@ -688,6 +698,11 @@ void SquishTools::executeRunnerQuery() cmdLine.addArg("--config"); cmdLine.addArg("getGlobalScriptDirs"); break; + case SetGlobalScriptDirs: + cmdLine.addArg("--config"); + cmdLine.addArg("setGlobalScriptDirs"); + cmdLine.addArgs(m_queryParameter, Utils::CommandLine::Raw); + break; default: QTC_ASSERT(false, return); } @@ -721,6 +736,7 @@ void SquishTools::onRunnerFinished() setState(RunnerStopped); m_fullRunnerOutput.clear(); m_queryCallback = {}; + m_queryParameter.clear(); return; } diff --git a/src/plugins/squish/squishtools.h b/src/plugins/squish/squishtools.h index 22b71c01038..1f459d95d08 100644 --- a/src/plugins/squish/squishtools.h +++ b/src/plugins/squish/squishtools.h @@ -70,6 +70,7 @@ public: const SuiteConf &suiteConf); void queryGlobalScripts(QueryCallback callback); void queryServerSettings(QueryCallback callback); + void requestSetSharedFolders(const Utils::FilePaths &sharedFolders); void writeServerSettingsChanges(const QList &changes); void requestExpansion(const QString &name); @@ -99,7 +100,7 @@ private: KillOldBeforeQueryRunner }; - enum RunnerQuery { ServerInfo, GetGlobalScriptDirs }; + enum RunnerQuery { ServerInfo, GetGlobalScriptDirs, SetGlobalScriptDirs }; void setState(State state); void handleSetStateStartAppRunner(); @@ -155,6 +156,7 @@ private: Utils::FilePaths m_reportFiles; Utils::FilePath m_currentResultsDirectory; QString m_fullRunnerOutput; // used when querying the server + QString m_queryParameter; Utils::FilePath m_currentTestCasePath; Utils::FilePath m_currentRecorderSnippetFile; QFile *m_currentResultsXML = nullptr; From 24d11e66b47e167413aabd308b381d35dcdb5447 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 14 Oct 2022 10:53:03 +0200 Subject: [PATCH 072/143] Utils: Fix FilePath::isChildOf Previously isChildOf would only look at the path. But if the two paths are from different devices it would mistakenly return true if the paths were similar. Change-Id: Icdb1aebe61167183ec85fa56ae0708681a59a9f8 Reviewed-by: hjk --- src/libs/utils/filepath.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index f5e86d922d0..dbcca7b6b0b 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -908,6 +908,8 @@ FilePath FilePath::operator+(const QString &s) const /// \returns whether FilePath is a child of \a s bool FilePath::isChildOf(const FilePath &s) const { + if (!s.isSameDevice(*this)) + return false; if (s.isEmpty()) return false; if (!path().startsWith(s.path(), caseSensitivity())) From 0b40e4b03284365ba3a8ce3f3fac87dd75995acd Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 14 Oct 2022 09:46:32 +0200 Subject: [PATCH 073/143] Utils: Fix FilePath::toFileInfo() Change-Id: I91f75511758e606c9e9ff1338b272e6b03c26986 Reviewed-by: hjk --- src/libs/utils/filepath.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index dbcca7b6b0b..90c99a48c37 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -146,8 +146,7 @@ FilePath FilePath::fromFileInfo(const QFileInfo &info) /// \returns a QFileInfo QFileInfo FilePath::toFileInfo() const { - QTC_ASSERT(!needsDevice(), return QFileInfo()); - return QFileInfo(cleanPath().path()); + return QFileInfo(toFSPathString()); } FilePath FilePath::fromUrl(const QUrl &url) From 9aa1d56f56e00f9ea658c9bd12db005375ad5805 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 14 Oct 2022 09:59:04 +0200 Subject: [PATCH 074/143] Docker: Fix isValidMountInfo Improves checking if a requested mount is actually valid. Change-Id: I37638d26ddd46776e6375481cf1d73ed0ad24210 Reviewed-by: hjk --- src/plugins/docker/dockerdevice.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index be7124c995e..1fd44e721a4 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -542,8 +542,18 @@ QStringList toMountArg(const DockerDevicePrivate::TemporaryMountInfo &mi) bool isValidMountInfo(const DockerDevicePrivate::TemporaryMountInfo &mi) { - return !mi.path.isEmpty() && !mi.containerPath.isEmpty() && mi.path.isAbsolutePath() - && mi.containerPath.isAbsolutePath(); + if (mi.path.needsDevice()) + return false; + + if (mi.path.isEmpty() || mi.containerPath.isEmpty()) + return false; + if (!mi.path.isAbsolutePath() || !mi.containerPath.isAbsolutePath()) + return false; + + if (!mi.path.exists()) + return false; + + return true; } QStringList DockerDevicePrivate::createMountArgs() const @@ -817,6 +827,9 @@ bool DockerDevice::handlesFile(const FilePath &filePath) const bool DockerDevice::ensureReachable(const FilePath &other) const { + if (other.isSameDevice(rootPath())) + return true; + if (other.needsDevice()) return false; @@ -1087,8 +1100,12 @@ bool DockerDevicePrivate::addTemporaryMount(const FilePath &path, const FilePath if (alreadyAdded) return false; + const TemporaryMountInfo newMount{path, containerPath}; + + QTC_ASSERT(isValidMountInfo(newMount), return false); + qCDebug(dockerDeviceLog) << "Adding temporary mount:" << path; - m_temporaryMounts.append({path, containerPath}); + m_temporaryMounts.append(newMount); stopCurrentContainer(); // Force re-start with new mounts. return true; } From 359ad56e176ef59d7f72a51b24ab80df3f178497 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 14 Oct 2022 10:21:49 +0200 Subject: [PATCH 075/143] Docker: combine escapeMountPath function Change-Id: Ib04f8245d2da096a9d9fdbdf9b5243ca80d52bfa Reviewed-by: hjk --- src/plugins/docker/dockerdevice.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 1fd44e721a4..96bdc53a64f 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -520,18 +520,21 @@ QString escapeMountPathWin(const FilePath &fp) return result; } +QString escapeMountPath(const FilePath &fp) +{ + if (HostOsInfo::isWindowsHost()) + return escapeMountPathWin(fp); + + return escapeMountPathUnix(fp); +} + QStringList toMountArg(const DockerDevicePrivate::TemporaryMountInfo &mi) { QString escapedPath; QString escapedContainerPath; - if (HostOsInfo::isWindowsHost()) { - escapedPath = escapeMountPathWin(mi.path); - escapedContainerPath = escapeMountPathWin(mi.containerPath); - } else { - escapedPath = escapeMountPathUnix(mi.path); - escapedContainerPath = escapeMountPathUnix(mi.containerPath); - } + escapedPath = escapeMountPath(mi.path); + escapedContainerPath = escapeMountPath(mi.containerPath); const QString mountArg = QString(R"(type=bind,"source=%1","destination=%2")") .arg(escapedPath) From 7a967d385ea903ca8f707dd5d47851085098539c Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 13 Oct 2022 16:25:23 +0300 Subject: [PATCH 076/143] QmlDesigner: Add unimportFinished signal to BundleImporter Change-Id: I6d7ab5716f1bf6fe48f454d0c95c29ebd5d181de Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Bot --- .../materialbrowser/bundleimporter.cpp | 51 ++++++++++++++----- .../materialbrowser/bundleimporter.h | 3 +- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/plugins/qmldesigner/components/materialbrowser/bundleimporter.cpp b/src/plugins/qmldesigner/components/materialbrowser/bundleimporter.cpp index 67559e08299..77c2c5ed302 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/bundleimporter.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/bundleimporter.cpp @@ -97,9 +97,10 @@ QString BundleImporter::importComponent(const QString &qmlFile, FilePath qmlSourceFile = bundleImportPath.resolvePath(FilePath::fromString(qmlFile)); const bool qmlFileExists = qmlSourceFile.exists(); const QString qmlType = qmlSourceFile.baseName(); - m_pendingTypes.append(QStringLiteral("%1.%2.%3") - .arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), - m_bundleId, qmlType)); + const QString fullTypeName = QStringLiteral("%1.%2.%3") + .arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), m_bundleId, qmlType); + if (m_pendingTypes.contains(fullTypeName) && !m_pendingTypes[fullTypeName]) + return QStringLiteral("Unable to import while unimporting the same type: '%1'").arg(fullTypeName); if (!qmldirContent.contains(qmlFile)) { qmldirContent.append(qmlType); qmldirContent.append(" 1.0 "); @@ -162,6 +163,7 @@ QString BundleImporter::importComponent(const QString &qmlFile, m_importAddPending = true; } } + m_pendingTypes.insert(fullTypeName, true); m_importTimerCount = 0; m_importTimer.start(); @@ -175,8 +177,16 @@ void BundleImporter::handleImportTimer() m_fullReset = false; m_importAddPending = false; m_importTimerCount = 0; - m_pendingTypes.clear(); - emit importFinished({}); + + // Emit dummy finished signals for all pending types + const QStringList pendingTypes = m_pendingTypes.keys(); + for (const QString &pendingType : pendingTypes) { + m_pendingTypes.remove(pendingType); + if (m_pendingTypes[pendingType]) + emit importFinished({}); + else + emit unimportFinished({}); + } }; auto doc = QmlDesignerPlugin::instance()->currentDesignDocument(); @@ -210,12 +220,17 @@ void BundleImporter::handleImportTimer() } // Detect when the code model has the new material(s) fully available - const QStringList pendingTypes = m_pendingTypes; + const QStringList pendingTypes = m_pendingTypes.keys(); for (const QString &pendingType : pendingTypes) { NodeMetaInfo metaInfo = model->metaInfo(pendingType.toUtf8()); - if (metaInfo.isValid() && !metaInfo.superClasses().isEmpty()) { - m_pendingTypes.removeAll(pendingType); - emit importFinished(metaInfo); + const bool isImport = m_pendingTypes[pendingType]; + const bool typeComplete = metaInfo.isValid() && !metaInfo.superClasses().isEmpty(); + if (isImport == typeComplete) { + m_pendingTypes.remove(pendingType); + if (isImport) + emit importFinished(metaInfo); + else + emit unimportFinished(metaInfo); } } @@ -257,14 +272,14 @@ QString BundleImporter::unimportComponent(const QString &qmlFile) { FilePath bundleImportPath = resolveBundleImportPath(); if (bundleImportPath.isEmpty()) - return "Failed to resolve bundle import folder"; + return QStringLiteral("Failed to resolve bundle import folder for: '%1'").arg(qmlFile); if (!bundleImportPath.exists()) - return {}; + return QStringLiteral("Unable to find bundle path: '%1'").arg(bundleImportPath.toString()); FilePath qmlFilePath = bundleImportPath.resolvePath(qmlFile); if (!qmlFilePath.exists()) - return {}; + return QStringLiteral("Unable to find specified file: '%1'").arg(qmlFilePath.toString()); QStringList removedFiles; removedFiles.append(qmlFile); @@ -272,9 +287,15 @@ QString BundleImporter::unimportComponent(const QString &qmlFile) FilePath qmldirPath = bundleImportPath.resolvePath(QStringLiteral("qmldir")); QByteArray qmldirContent = qmldirPath.fileContents(); QByteArray newContent; + + QString qmlType = qmlFilePath.baseName(); + const QString fullTypeName = QStringLiteral("%1.%2.%3") + .arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), m_bundleId, qmlType); + if (m_pendingTypes.contains(fullTypeName) && m_pendingTypes[fullTypeName]) + return QStringLiteral("Unable to unimport while importing the same type: '%1'").arg(fullTypeName); + if (!qmldirContent.isEmpty()) { - QByteArray qmlType = qmlFilePath.baseName().toUtf8(); - int typeIndex = qmldirContent.indexOf(qmlType); + int typeIndex = qmldirContent.indexOf(qmlType.toUtf8()); if (typeIndex != -1) { int newLineIndex = qmldirContent.indexOf('\n', typeIndex); newContent = qmldirContent.left(typeIndex); @@ -287,6 +308,8 @@ QString BundleImporter::unimportComponent(const QString &qmlFile) } } + m_pendingTypes.insert(fullTypeName, false); + QVariantHash assetRefMap = loadAssetRefMap(bundleImportPath); bool writeAssetRefs = false; const auto keys = assetRefMap.keys(); diff --git a/src/plugins/qmldesigner/components/materialbrowser/bundleimporter.h b/src/plugins/qmldesigner/components/materialbrowser/bundleimporter.h index b22d0edd591..599d4d8e50b 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/bundleimporter.h +++ b/src/plugins/qmldesigner/components/materialbrowser/bundleimporter.h @@ -57,6 +57,7 @@ signals: // asynchronous part of the import. In this case all remaining pending imports have been // terminated, and will not receive separate importFinished notifications. void importFinished(const QmlDesigner::NodeMetaInfo &metaInfo); + void unimportFinished(const QmlDesigner::NodeMetaInfo &metaInfo); private: void handleImportTimer(); @@ -72,7 +73,7 @@ private: int m_importTimerCount = 0; bool m_importAddPending = false; bool m_fullReset = false; - QStringList m_pendingTypes; + QHash m_pendingTypes; // }; } // namespace QmlDesigner::Internal From 967e8cf26067d1a41c11e69d0878d1e2b3474b77 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Thu, 13 Oct 2022 22:40:27 +0300 Subject: [PATCH 077/143] Git: Respect text encoding in project settings on diff Fixes: QTCREATORBUG-21794 Change-Id: Ib4be9811c0ab1cba5cad4adbfe740a6d99f420d1 Reviewed-by: Jarek Kobus --- src/plugins/git/gitclient.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index 50ba5047813..5996d7f4947 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -278,7 +278,8 @@ public: : GitBaseDiffEditorController(document, leftCommit, rightCommit) { setReloader([this, extraArgs] { - runCommand({addConfigurationArguments(baseArguments() << extraArgs)}); + runCommand({addConfigurationArguments(baseArguments() << extraArgs)}, + VcsBaseEditor::getCodec(workingDirectory(), {})); }); } }; @@ -413,7 +414,7 @@ public: argLists << addConfigurationArguments(baseArguments() << "--" << unstagedFiles); if (!argLists.isEmpty()) - runCommand(argLists); + runCommand(argLists, VcsBaseEditor::getCodec(workingDirectory(), stagedFiles + unstagedFiles)); }); } }; From 22c4e9318a1823bd0f6d3569da1bcb31b2eb0d5d Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 14 Oct 2022 13:59:50 +0200 Subject: [PATCH 078/143] Docker: Remove unused function Change-Id: I683de49c08411087e47be84265cbab62d4d0738e Reviewed-by: hjk --- src/plugins/docker/dockerdevice.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 96bdc53a64f..6a8c604feda 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -145,9 +145,6 @@ public: ~DockerDevicePrivate() { stopCurrentContainer(); } RunResult runInShell(const CommandLine &cmd, const QByteArray &stdInData = {}); - bool runInShellSuccess(const CommandLine &cmd, const QByteArray &stdInData = {}) { - return runInShell(cmd, stdInData).exitCode == 0; - } void updateContainerAccess(); void changeMounts(QStringList newMounts); From 183dfb48a71c6bf606cd401aa06c4ccd66e9ef36 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Fri, 14 Oct 2022 14:41:39 +0300 Subject: [PATCH 079/143] VCS: Rename function Change-Id: I593e1db222e2124f3db367620171f3a101b4b8eb Reviewed-by: Jarek Kobus --- src/plugins/vcsbase/submiteditorwidget.cpp | 2 +- src/plugins/vcsbase/submiteditorwidget.h | 2 +- src/plugins/vcsbase/vcsbasesubmiteditor.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/vcsbase/submiteditorwidget.cpp b/src/plugins/vcsbase/submiteditorwidget.cpp index 4a4b806915d..a2b751433b3 100644 --- a/src/plugins/vcsbase/submiteditorwidget.cpp +++ b/src/plugins/vcsbase/submiteditorwidget.cpp @@ -645,7 +645,7 @@ bool SubmitEditorWidget::canSubmit(QString *whyNot) const return res; } -bool SubmitEditorWidget::edited() const +bool SubmitEditorWidget::isEdited() const { return !d->m_description.trimmed().isEmpty() || checkedFilesCount() > 0; } diff --git a/src/plugins/vcsbase/submiteditorwidget.h b/src/plugins/vcsbase/submiteditorwidget.h index 2f0a8f8a070..d3a2c290ae6 100644 --- a/src/plugins/vcsbase/submiteditorwidget.h +++ b/src/plugins/vcsbase/submiteditorwidget.h @@ -67,7 +67,7 @@ public: QList submitFieldWidgets() const; virtual bool canSubmit(QString *whyNot = nullptr) const; - bool edited() const; + bool isEdited() const; void setUpdateInProgress(bool value); bool updateInProgress() const; diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp index a11b9b4375c..5ce7258b558 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp @@ -509,7 +509,7 @@ VcsBaseSubmitEditor::PromptSubmitResult Core::EditorManager::activateEditor(this, Core::EditorManager::IgnoreNavigationHistory); - if (!submitWidget->isEnabled() || !submitWidget->edited()) + if (!submitWidget->isEnabled() || !submitWidget->isEdited()) return SubmitDiscarded; QString errorMessage; From 679a726330fdf1d1f122320872f9f033d894799e Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 7 Oct 2022 18:00:36 +0200 Subject: [PATCH 080/143] C++ support: Consider project-specific target triple ... before creating project parts. Otherwise we can get wrong includes and defines from the compiler. Amends 9c86e6746fb82d6244cc2fc565d5f730c67f8106. Also do not add -m32 or -m64 for non-x86 targets. Task-number: QTCREATORBUG-25615 Change-Id: I02da9251c77d45fc8827990a2d59c3ae2c262591 Reviewed-by: Qt CI Bot Reviewed-by: Reviewed-by: Christian Stenger Reviewed-by: Eike Ziller --- src/plugins/clangcodemodel/clangutils.cpp | 5 ++--- .../cppeditor/compileroptionsbuilder.cpp | 4 +++- .../cppeditor/compileroptionsbuilder_test.cpp | 6 +++++- .../cppeditor/cppcodemodelinspectordialog.cpp | 2 +- .../cppeditor/cppcodemodelinspectordumper.cpp | 12 +++++------ .../cppeditor/cppcodemodelinspectordumper.h | 2 +- .../cppeditor/cppprojectinfogenerator.cpp | 21 +++++++++++++++++++ src/plugins/cppeditor/projectpart.cpp | 3 +-- src/plugins/cppeditor/projectpart.h | 7 +------ .../projectexplorer/rawprojectpart.cpp | 2 +- src/plugins/projectexplorer/rawprojectpart.h | 2 +- 11 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp index a50d7ec9b31..fc7c174cbda 100644 --- a/src/plugins/clangcodemodel/clangutils.cpp +++ b/src/plugins/clangcodemodel/clangutils.cpp @@ -78,9 +78,8 @@ static QStringList projectPartArguments(const ProjectPart &projectPart) args << "-c"; if (projectPart.toolchainType != ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) { args << "--target=" + projectPart.toolChainTargetTriple; - args << (projectPart.toolChainWordWidth == ProjectPart::WordWidth64Bit - ? QLatin1String("-m64") - : QLatin1String("-m32")); + if (projectPart.toolChainAbi.architecture() == Abi::X86Architecture) + args << QLatin1String(projectPart.toolChainAbi.wordWidth() == 64 ? "-m64" : "-m32"); } args << projectPart.compilerFlags; for (const ProjectExplorer::HeaderPath &headerPath : projectPart.headerPaths) { diff --git a/src/plugins/cppeditor/compileroptionsbuilder.cpp b/src/plugins/cppeditor/compileroptionsbuilder.cpp index a1b151de2a2..def318acb4e 100644 --- a/src/plugins/cppeditor/compileroptionsbuilder.cpp +++ b/src/plugins/cppeditor/compileroptionsbuilder.cpp @@ -227,7 +227,9 @@ QStringList createLanguageOptionGcc(ProjectFile::Kind fileKind, bool objcExt) void CompilerOptionsBuilder::addWordWidth() { - const QString argument = m_projectPart.toolChainWordWidth == ProjectPart::WordWidth64Bit + if (m_projectPart.toolChainAbi.architecture() != Abi::X86Architecture) + return; + const QString argument = m_projectPart.toolChainAbi.wordWidth() == 64 ? QLatin1String("-m64") : QLatin1String("-m32"); add(argument); diff --git a/src/plugins/cppeditor/compileroptionsbuilder_test.cpp b/src/plugins/cppeditor/compileroptionsbuilder_test.cpp index 2bc07375842..f4d84e4e03b 100644 --- a/src/plugins/cppeditor/compileroptionsbuilder_test.cpp +++ b/src/plugins/cppeditor/compileroptionsbuilder_test.cpp @@ -37,8 +37,12 @@ public: rpp.setConfigFileName(projectConfigFile); ToolChainInfo tcInfo; tcInfo.type = toolchainType; - tcInfo.wordWidth = 64; tcInfo.targetTriple = targetTriple; + tcInfo.abi = Abi::fromString(targetTriple); + if (!tcInfo.abi.isValid()) { + tcInfo.abi = Abi(Abi::X86Architecture, Abi::DarwinOS, Abi::FreeBsdFlavor, + Abi::MachOFormat, 64); + } tcInfo.isMsvc2015ToolChain = isMsvc2015; tcInfo.extraCodeModelFlags = extraFlags; tcInfo.macroInspectionRunner = [this](const QStringList &) { diff --git a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp index f17f5c1f62f..0b5006e6bff 100644 --- a/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp +++ b/src/plugins/cppeditor/cppcodemodelinspectordialog.cpp @@ -1789,7 +1789,7 @@ void CppCodeModelInspectorDialog::updateProjectPartData(const ProjectPart::Const {QString::fromLatin1("Build Target Type"), CMI::Utils::toString(part->buildTargetType)}, {QString::fromLatin1("ToolChain Type"), part->toolchainType.toString()}, {QString::fromLatin1("ToolChain Target Triple"), part->toolChainTargetTriple}, - {QString::fromLatin1("ToolChain Word Width"), CMI::Utils::toString(part->toolChainWordWidth)}, + {QString::fromLatin1("ToolChain Word Width"), CMI::Utils::toString(part->toolChainAbi.wordWidth())}, {QString::fromLatin1("ToolChain Install Dir"), part->toolChainInstallDir.toString()}, {QString::fromLatin1("Language Version"), CMI::Utils::toString(part->languageVersion)}, {QString::fromLatin1("Language Extensions"), CMI::Utils::toString(part->languageExtensions)}, diff --git a/src/plugins/cppeditor/cppcodemodelinspectordumper.cpp b/src/plugins/cppeditor/cppcodemodelinspectordumper.cpp index 6b4429574e8..b82c2a03e11 100644 --- a/src/plugins/cppeditor/cppcodemodelinspectordumper.cpp +++ b/src/plugins/cppeditor/cppcodemodelinspectordumper.cpp @@ -373,15 +373,15 @@ QString Utils::toString(CPlusPlus::Kind kind) return QString(); } -QString Utils::toString(ProjectPart::ToolChainWordWidth width) +QString Utils::toString(const ProjectExplorer::Abi &abi) { - switch (width) { - case ProjectPart::ToolChainWordWidth::WordWidth32Bit: + switch (abi.wordWidth()) { + case 32: return QString("32"); - case ProjectPart::ToolChainWordWidth::WordWidth64Bit: + case 64: return QString("64"); } - return QString(); + return QString("??"); } QString Utils::partsForFile(const QString &fileName) @@ -508,7 +508,7 @@ void Dumper::dumpProjectInfos(const QList &projectInfos) m_out << i3 << "Project File : " << projectFilePath << "\n"; m_out << i3 << "ToolChain Type : " << part->toolchainType.toString() << "\n"; m_out << i3 << "ToolChain Target Triple: " << part->toolChainTargetTriple << "\n"; - m_out << i3 << "ToolChain Word Width : " << part->toolChainWordWidth << "\n"; + m_out << i3 << "ToolChain Word Width : " << part->toolChainAbi.wordWidth() << "\n"; m_out << i3 << "ToolChain Install Dir : " << part->toolChainInstallDir << "\n"; m_out << i3 << "Compiler Flags : " << part->compilerFlags.join(", ") << "\n"; m_out << i3 << "Selected For Building : " << part->selectedForBuilding << "\n"; diff --git a/src/plugins/cppeditor/cppcodemodelinspectordumper.h b/src/plugins/cppeditor/cppcodemodelinspectordumper.h index 9474988af0c..872c868fee0 100644 --- a/src/plugins/cppeditor/cppcodemodelinspectordumper.h +++ b/src/plugins/cppeditor/cppcodemodelinspectordumper.h @@ -32,7 +32,7 @@ struct Utils static QString toString(const QVector &projectFiles); static QString toString(ProjectFile::Kind kind); static QString toString(CPlusPlus::Kind kind); - static QString toString(ProjectPart::ToolChainWordWidth width); + static QString toString(const ProjectExplorer::Abi &abi); static QString partsForFile(const QString &fileName); static QString unresolvedFileNameWithDelimiters(const CPlusPlus::Document::Include &include); static QString pathListToString(const QStringList &pathList); diff --git a/src/plugins/cppeditor/cppprojectinfogenerator.cpp b/src/plugins/cppeditor/cppprojectinfogenerator.cpp index 629f44c43a1..2e5c068bd77 100644 --- a/src/plugins/cppeditor/cppprojectinfogenerator.cpp +++ b/src/plugins/cppeditor/cppprojectinfogenerator.cpp @@ -137,6 +137,27 @@ ProjectPart::ConstPtr ProjectInfoGenerator::createProjectPart( tcInfo = m_projectUpdateInfo.cxxToolChainInfo; } + QString explicitTarget; + if (!tcInfo.targetTripleIsAuthoritative) { + for (int i = 0; i < flags.commandLineFlags.size(); ++i) { + const QString &flag = flags.commandLineFlags.at(i); + if (flag == "-target") { + if (i + 1 < flags.commandLineFlags.size()) + explicitTarget = flags.commandLineFlags.at(i + 1); + break; + } else if (flag.startsWith("--target=")) { + explicitTarget = flag.mid(9); + break; + } + } + } + if (!explicitTarget.isEmpty()) { + tcInfo.targetTriple = explicitTarget; + tcInfo.targetTripleIsAuthoritative = true; + if (const Abi abi = Abi::fromString(tcInfo.targetTriple); abi.isValid()) + tcInfo.abi = abi; + } + return ProjectPart::create(projectFilePath, rawProjectPart, partName, projectFiles, language, languageExtensions, flags, tcInfo); } diff --git a/src/plugins/cppeditor/projectpart.cpp b/src/plugins/cppeditor/projectpart.cpp index 464c138cc8b..a315b621b8b 100644 --- a/src/plugins/cppeditor/projectpart.cpp +++ b/src/plugins/cppeditor/projectpart.cpp @@ -145,8 +145,7 @@ ProjectPart::ProjectPart(const Utils::FilePath &topLevelProject, isMsvc2015Toolchain(tcInfo.isMsvc2015ToolChain), toolChainTargetTriple(tcInfo.targetTriple), targetTripleIsAuthoritative(tcInfo.targetTripleIsAuthoritative), - toolChainWordWidth(tcInfo.wordWidth == 64 ? ProjectPart::WordWidth64Bit - : ProjectPart::WordWidth32Bit), + toolChainAbi(tcInfo.abi), toolChainInstallDir(tcInfo.installDir), compilerFilePath(tcInfo.compilerFilePath), warningFlags(flags.warningFlags), diff --git a/src/plugins/cppeditor/projectpart.h b/src/plugins/cppeditor/projectpart.h index 6f99dcf6d7e..4845d796a25 100644 --- a/src/plugins/cppeditor/projectpart.h +++ b/src/plugins/cppeditor/projectpart.h @@ -28,11 +28,6 @@ namespace CppEditor { class CPPEDITOR_EXPORT ProjectPart { public: - enum ToolChainWordWidth { - WordWidth32Bit, - WordWidth64Bit, - }; - using ConstPtr = QSharedPointer; public: @@ -94,7 +89,7 @@ public: const bool isMsvc2015Toolchain = false; const QString toolChainTargetTriple; const bool targetTripleIsAuthoritative; - const ToolChainWordWidth toolChainWordWidth = WordWidth32Bit; + const ProjectExplorer::Abi toolChainAbi = ProjectExplorer::Abi::hostAbi(); const Utils::FilePath toolChainInstallDir; const Utils::FilePath compilerFilePath; const Utils::WarningFlags warningFlags = Utils::WarningFlags::Default; diff --git a/src/plugins/projectexplorer/rawprojectpart.cpp b/src/plugins/projectexplorer/rawprojectpart.cpp index 1f7cea32c96..673a19e859c 100644 --- a/src/plugins/projectexplorer/rawprojectpart.cpp +++ b/src/plugins/projectexplorer/rawprojectpart.cpp @@ -160,7 +160,7 @@ ToolChainInfo::ToolChainInfo(const ToolChain *toolChain, // Keep the following cheap/non-blocking for the ui thread... type = toolChain->typeId(); isMsvc2015ToolChain = toolChain->targetAbi().osFlavor() == Abi::WindowsMsvc2015Flavor; - wordWidth = toolChain->targetAbi().wordWidth(); + abi = toolChain->targetAbi(); targetTriple = toolChain->effectiveCodeModelTargetTriple(); targetTripleIsAuthoritative = !toolChain->explicitCodeModelTargetTriple().isEmpty(); extraCodeModelFlags = toolChain->extraCodeModelFlags(); diff --git a/src/plugins/projectexplorer/rawprojectpart.h b/src/plugins/projectexplorer/rawprojectpart.h index e1895869294..6b06cdc6d82 100644 --- a/src/plugins/projectexplorer/rawprojectpart.h +++ b/src/plugins/projectexplorer/rawprojectpart.h @@ -141,7 +141,7 @@ public: Utils::Id type; bool isMsvc2015ToolChain = false; bool targetTripleIsAuthoritative = false; - unsigned wordWidth = 0; + Abi abi; QString targetTriple; Utils::FilePath compilerFilePath; Utils::FilePath installDir; From a1e960a27ceee7548db401184bfe8f8786de2e8d Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 14 Oct 2022 13:39:16 +0200 Subject: [PATCH 081/143] Utils: Fix iterateWithFind The change of runInShell to take QString + QStringList meant that the caller was no longer able to fine tune the argument escaping. This lead to "find -exec ... \;" being escaped incorrectly. Changing back to CommandLine fixes this. Change-Id: I55b09bd745c09912a2a0b4e43432824a99c0dd4e Reviewed-by: Jarek Kobus Reviewed-by: hjk --- src/libs/utils/devicefileaccess.cpp | 67 ++++++++++++------------- src/libs/utils/devicefileaccess.h | 11 ++-- src/plugins/docker/dockerdevice.cpp | 25 ++------- src/plugins/remotelinux/linuxdevice.cpp | 8 ++- 4 files changed, 43 insertions(+), 68 deletions(-) diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp index 670570e86af..956b524e724 100644 --- a/src/libs/utils/devicefileaccess.cpp +++ b/src/libs/utils/devicefileaccess.cpp @@ -6,6 +6,7 @@ #include "algorithm.h" #include "qtcassert.h" #include "hostosinfo.h" +#include "commandline.h" #include #include @@ -619,83 +620,81 @@ OsType DesktopDeviceFileAccess::osType(const FilePath &filePath) const UnixDeviceFileAccess::~UnixDeviceFileAccess() = default; -bool UnixDeviceFileAccess::runInShellSuccess( - const QString &executable, - const QStringList &args, - const QByteArray &stdInData) const +bool UnixDeviceFileAccess::runInShellSuccess(const CommandLine &cmdLine, + const QByteArray &stdInData) const { - return runInShell(executable, args, stdInData).exitCode == 0; + return runInShell(cmdLine, stdInData).exitCode == 0; } bool UnixDeviceFileAccess::isExecutableFile(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess("test", {"-x", path}); + return runInShellSuccess({"test", {"-x", path}}); } bool UnixDeviceFileAccess::isReadableFile(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess("test", {"-r", path, "-a", "-f", path}); + return runInShellSuccess({"test", {"-r", path, "-a", "-f", path}}); } bool UnixDeviceFileAccess::isWritableFile(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess("test", {"-w", path, "-a", "-f", path}); + return runInShellSuccess({"test", {"-w", path, "-a", "-f", path}}); } bool UnixDeviceFileAccess::isReadableDirectory(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess("test", {"-r", path, "-a", "-d", path}); + return runInShellSuccess({"test", {"-r", path, "-a", "-d", path}}); } bool UnixDeviceFileAccess::isWritableDirectory(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess("test", {"-w", path, "-a", "-d", path}); + return runInShellSuccess({"test", {"-w", path, "-a", "-d", path}}); } bool UnixDeviceFileAccess::isFile(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess("test", {"-f", path}); + return runInShellSuccess({"test", {"-f", path}}); } bool UnixDeviceFileAccess::isDirectory(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess("test", {"-d", path}); + return runInShellSuccess({"test", {"-d", path}}); } bool UnixDeviceFileAccess::isSymLink(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess("test", {"-h", path}); + return runInShellSuccess({"test", {"-h", path}}); } bool UnixDeviceFileAccess::ensureExistingFile(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess("touch", {path}); + return runInShellSuccess({"touch", {path}}); } bool UnixDeviceFileAccess::createDirectory(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess("mkdir", {"-p", path}); + return runInShellSuccess({"mkdir", {"-p", path}}); } bool UnixDeviceFileAccess::exists(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess("test", {"-e", path}); + return runInShellSuccess({"test", {"-e", path}}); } bool UnixDeviceFileAccess::removeFile(const FilePath &filePath) const { - return runInShellSuccess("rm", {filePath.path()}); + return runInShellSuccess({"rm", {filePath.path()}}); } bool UnixDeviceFileAccess::removeRecursively(const FilePath &filePath, QString *error) const @@ -711,7 +710,7 @@ bool UnixDeviceFileAccess::removeRecursively(const FilePath &filePath, QString * levelsNeeded = 2; QTC_ASSERT(path.count('/') >= levelsNeeded, return false); - RunResult result = runInShell("rm", {"-rf", "--", path}); + RunResult result = runInShell({"rm", {"-rf", "--", path}}); if (error) *error = QString::fromUtf8(result.stdErr); return result.exitCode == 0; @@ -719,17 +718,17 @@ bool UnixDeviceFileAccess::removeRecursively(const FilePath &filePath, QString * bool UnixDeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const { - return runInShellSuccess("cp", {filePath.path(), target.path()}); + return runInShellSuccess({"cp", {filePath.path(), target.path()}}); } bool UnixDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const { - return runInShellSuccess("mv", {filePath.path(), target.path()}); + return runInShellSuccess({"mv", {filePath.path(), target.path()}}); } FilePath UnixDeviceFileAccess::symLinkTarget(const FilePath &filePath) const { - const RunResult result = runInShell("readlink", {"-n", "-e", filePath.path()}); + const RunResult result = runInShell({"readlink", {"-n", "-e", filePath.path()}}); const QString out = QString::fromUtf8(result.stdOut); return out.isEmpty() ? FilePath() : filePath.withNewPath(out); } @@ -747,7 +746,7 @@ std::optional UnixDeviceFileAccess::fileContents( args += QString("seek=%1").arg(offset / gcd); } - const RunResult r = runInShell("dd", args); + const RunResult r = runInShell({"dd", args}); if (r.exitCode != 0) return {}; @@ -765,7 +764,7 @@ bool UnixDeviceFileAccess::writeFileContents( args.append("bs=1"); args.append(QString("seek=%1").arg(offset)); } - return runInShellSuccess("dd", args, data); + return runInShellSuccess({"dd", args}, data); } OsType UnixDeviceFileAccess::osType(const FilePath &filePath) const @@ -776,7 +775,7 @@ OsType UnixDeviceFileAccess::osType(const FilePath &filePath) const QDateTime UnixDeviceFileAccess::lastModified(const FilePath &filePath) const { - const RunResult result = runInShell("stat", {"-L", "-c", "%Y", filePath.path()}); + const RunResult result = runInShell({"stat", {"-L", "-c", "%Y", filePath.path()}}); qint64 secs = result.stdOut.toLongLong(); const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC); return dt; @@ -784,7 +783,7 @@ QDateTime UnixDeviceFileAccess::lastModified(const FilePath &filePath) const QFile::Permissions UnixDeviceFileAccess::permissions(const FilePath &filePath) const { - const RunResult result = runInShell("stat", {"-L", "-c", "%a", filePath.path()}); + const RunResult result = runInShell({"stat", {"-L", "-c", "%a", filePath.path()}}); const uint bits = result.stdOut.toUInt(nullptr, 8); QFileDevice::Permissions perm = {}; #define BIT(n, p) if (bits & (1< fileContents( - const FilePath &filePath, - qint64 limit, - qint64 offset) const override; - DockerDevicePrivate *m_dev = nullptr; }; @@ -352,22 +346,11 @@ Tasks DockerDevicePrivate::validateMounts() const return result; } -RunResult DockerDeviceFileAccess::runInShell( - const QString &executable, - const QStringList &arguments, - const QByteArray &stdInData) const +RunResult DockerDeviceFileAccess::runInShell(const CommandLine &cmdLine, + const QByteArray &stdInData) const { QTC_ASSERT(m_dev, return {}); - return m_dev->runInShell({FilePath::fromString(executable), arguments}, stdInData); -} - -std::optional DockerDeviceFileAccess::fileContents( - const FilePath &filePath, - qint64 limit, - qint64 offset) const -{ - m_dev->updateContainerAccess(); - return UnixDeviceFileAccess::fileContents(filePath, limit, offset); + return m_dev->runInShell(cmdLine, stdInData); } DockerDevice::DockerDevice(DockerSettings *settings, const DockerDeviceData &data) diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index b5b9205377d..5f820126d95 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -359,8 +359,7 @@ public: : m_dev(dev) {} - RunResult runInShell(const QString &executable, - const QStringList &arguments, + RunResult runInShell(const CommandLine &cmdLine, const QByteArray &stdInData) const override; LinuxDevicePrivate *m_dev; @@ -386,11 +385,10 @@ public: LinuxDeviceFileAccess m_fileAccess{this}; }; -RunResult LinuxDeviceFileAccess::runInShell(const QString &executable, - const QStringList &arguments, +RunResult LinuxDeviceFileAccess::runInShell(const CommandLine &cmdLine, const QByteArray &stdInData) const { - return m_dev->runInShell({FilePath::fromString(executable), arguments}, stdInData); + return m_dev->runInShell(cmdLine, stdInData); } // SshProcessImpl From ec430787e5aa1f24d678c6b62244eb33f3725112 Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 14 Oct 2022 14:23:25 +0200 Subject: [PATCH 082/143] CMake: More FilePath in Deployment data Change-Id: I898a621a34558a28d4688a33aeb9bb9148df6901 Reviewed-by: Cristian Adam --- .../cmakeprojectmanager/cmakebuildsystem.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index d2239ccaa9e..7c3db2c7403 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -1093,28 +1093,28 @@ DeploymentData CMakeBuildSystem::deploymentData() const { DeploymentData result; - QDir sourceDir = project()->projectDirectory().toString(); - QDir buildDir = buildConfiguration()->buildDirectory().toString(); + FilePath sourceDir = project()->projectDirectory(); + FilePath buildDir = buildConfiguration()->buildDirectory(); QString deploymentPrefix; - QString deploymentFilePath = sourceDir.filePath("QtCreatorDeployment.txt"); + FilePath deploymentFilePath = sourceDir.pathAppended("QtCreatorDeployment.txt"); - bool hasDeploymentFile = QFileInfo::exists(deploymentFilePath); + bool hasDeploymentFile = deploymentFilePath.exists(); if (!hasDeploymentFile) { - deploymentFilePath = buildDir.filePath("QtCreatorDeployment.txt"); - hasDeploymentFile = QFileInfo::exists(deploymentFilePath); + deploymentFilePath = buildDir.pathAppended("QtCreatorDeployment.txt"); + hasDeploymentFile = deploymentFilePath.exists(); } if (!hasDeploymentFile) return result; - deploymentPrefix = result.addFilesFromDeploymentFile(deploymentFilePath, - sourceDir.absolutePath()); + deploymentPrefix = result.addFilesFromDeploymentFile(deploymentFilePath.toString(), + sourceDir.toString()); for (const CMakeBuildTarget &ct : m_buildTargets) { if (ct.targetType == ExecutableType || ct.targetType == DynamicLibraryType) { if (!ct.executable.isEmpty() && result.deployableForLocalFile(ct.executable).localFilePath() != ct.executable) { result.addFile(ct.executable, - deploymentPrefix + buildDir.relativeFilePath(ct.executable.toFileInfo().dir().path()), + deploymentPrefix + buildDir.relativeChildPath(ct.executable).toString(), DeployableFile::TypeExecutable); } } From 8db0d3b0ff5fd5ea191928d14441e13f387cb83a Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Fri, 14 Oct 2022 14:52:54 +0200 Subject: [PATCH 083/143] Fix lupdate issues - missing Q_DECLARE_TR_FUNCTIONS - some full qualifications needed for confused lupdate - avoid breaking some translations from former TaskListPlugin, VcsCommand and highlighter settings refactoring Change-Id: Ia3b34095512a7bad6903f0aff6095313ee39e3e4 Reviewed-by: hjk --- src/libs/utils/detailsbutton.h | 2 ++ src/plugins/clangtools/clangtool.h | 3 ++- src/plugins/cppcheck/cppcheckplugin.cpp | 3 ++- src/plugins/ctfvisualizer/ctfvisualizertool.h | 2 +- src/plugins/mesonprojectmanager/mesonactionsmanager.h | 3 ++- src/plugins/perfprofiler/perfprofilertool.h | 2 +- src/plugins/projectexplorer/projectexplorer.cpp | 2 +- src/plugins/projectexplorer/taskfile.cpp | 4 ++-- src/plugins/projectexplorer/taskfile.h | 1 + src/plugins/texteditor/highlightersettingspage.cpp | 1 + src/plugins/vcsbase/vcscommand.cpp | 4 ++-- 11 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/libs/utils/detailsbutton.h b/src/libs/utils/detailsbutton.h index 7a1ac96b04d..8895418f805 100644 --- a/src/libs/utils/detailsbutton.h +++ b/src/libs/utils/detailsbutton.h @@ -5,6 +5,7 @@ #include "utils_global.h" +#include #include QT_BEGIN_NAMESPACE @@ -44,6 +45,7 @@ public: class QTCREATOR_UTILS_EXPORT DetailsButton : public ExpandButton { + Q_DECLARE_TR_FUNCTIONS(Utils::DetailsButton); public: DetailsButton(QWidget *parent = nullptr); QSize sizeHint() const override; diff --git a/src/plugins/clangtools/clangtool.h b/src/plugins/clangtools/clangtool.h index bd33e31ebc6..34f5eafec15 100644 --- a/src/plugins/clangtools/clangtool.h +++ b/src/plugins/clangtools/clangtool.h @@ -161,7 +161,8 @@ private: QAction *m_clear = nullptr; QAction *m_expandCollapse = nullptr; - Utils::Perspective m_perspective{ClangTidyClazyPerspectiveId, tr("Clang-Tidy and Clazy")}; + Utils::Perspective m_perspective{ClangTidyClazyPerspectiveId, + ::ClangTools::Internal::ClangTool::tr("Clang-Tidy and Clazy")}; private: const QString m_name; diff --git a/src/plugins/cppcheck/cppcheckplugin.cpp b/src/plugins/cppcheck/cppcheckplugin.cpp index c40f1ee6c66..18c4df53481 100644 --- a/src/plugins/cppcheck/cppcheckplugin.cpp +++ b/src/plugins/cppcheck/cppcheckplugin.cpp @@ -39,7 +39,8 @@ public: CppcheckOptionsPage options; DiagnosticsModel manualRunModel; CppcheckTool manualRunTool; - Utils::Perspective perspective{Constants::PERSPECTIVE_ID, CppcheckPlugin::tr("Cppcheck")}; + Utils::Perspective perspective{Constants::PERSPECTIVE_ID, + ::Cppcheck::Internal::CppcheckPlugin::tr("Cppcheck")}; QAction *manualRunAction; void startManualRun(); diff --git a/src/plugins/ctfvisualizer/ctfvisualizertool.h b/src/plugins/ctfvisualizer/ctfvisualizertool.h index bd31a8042d3..b3f31cbb970 100644 --- a/src/plugins/ctfvisualizer/ctfvisualizertool.h +++ b/src/plugins/ctfvisualizer/ctfvisualizertool.h @@ -46,7 +46,7 @@ private: void toggleThreadRestriction(QAction *action); Utils::Perspective m_perspective{Constants::CtfVisualizerPerspectiveId, - Tr::tr("Chrome Trace Format Visualizer")}; + ::CtfVisualizer::Tr::tr("Chrome Trace Format Visualizer")}; bool m_isLoading; QScopedPointer m_loadJson; diff --git a/src/plugins/mesonprojectmanager/mesonactionsmanager.h b/src/plugins/mesonprojectmanager/mesonactionsmanager.h index 8cf00599f2c..55c1dd51814 100644 --- a/src/plugins/mesonprojectmanager/mesonactionsmanager.h +++ b/src/plugins/mesonprojectmanager/mesonactionsmanager.h @@ -14,7 +14,8 @@ class MesonActionsManager : public QObject { Q_OBJECT Utils::ParameterAction buildTargetContextAction{ - Tr::tr("Build"), Tr::tr("Build \"%1\""), + ::MesonProjectManager::Tr::tr("Build"), + ::MesonProjectManager::Tr::tr("Build \"%1\""), Utils::ParameterAction::AlwaysEnabled /*handled manually*/ }; QAction configureActionMenu; diff --git a/src/plugins/perfprofiler/perfprofilertool.h b/src/plugins/perfprofiler/perfprofilertool.h index bfa6598ca0a..4face4dfe5e 100644 --- a/src/plugins/perfprofiler/perfprofilertool.h +++ b/src/plugins/perfprofiler/perfprofilertool.h @@ -79,7 +79,7 @@ private: void finalize(); Utils::Perspective m_perspective{Constants::PerfProfilerPerspectiveId, - Tr::tr("Performance Analyzer")}; + ::PerfProfiler::Tr::tr("Performance Analyzer")}; QAction *m_startAction = nullptr; QAction *m_stopAction = nullptr; diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index db57d6f2bca..c6ee39013b0 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -2239,7 +2239,7 @@ void ProjectExplorerPlugin::extensionsInitialized() BuildManager::extensionsInitialized(); TaskHub::addCategory(Constants::TASK_CATEGORY_SANITIZER, tr("Sanitizer", "Category for sanitizer issues listed under 'Issues'")); - TaskHub::addCategory(Constants::TASK_CATEGORY_TASKLIST_ID, tr("My Tasks")); + TaskHub::addCategory(Constants::TASK_CATEGORY_TASKLIST_ID, TaskFile::tr("My Tasks")); SshSettings::loadSettings(Core::ICore::settings()); const auto searchPathRetriever = [] { diff --git a/src/plugins/projectexplorer/taskfile.cpp b/src/plugins/projectexplorer/taskfile.cpp index c3f89cfc3b1..9ba6a3617a3 100644 --- a/src/plugins/projectexplorer/taskfile.cpp +++ b/src/plugins/projectexplorer/taskfile.cpp @@ -104,8 +104,8 @@ static bool parseTaskFile(QString *errorString, const FilePath &name) { QFile tf(name.toString()); if (!tf.open(QIODevice::ReadOnly)) { - *errorString = ProjectExplorerPlugin::tr("Cannot open task file %1: %2").arg( - name.toUserOutput(), tf.errorString()); + *errorString = TaskFile::tr("Cannot open task file %1: %2") + .arg(name.toUserOutput(), tf.errorString()); return false; } diff --git a/src/plugins/projectexplorer/taskfile.h b/src/plugins/projectexplorer/taskfile.h index 9676c1553d5..5286267dd3f 100644 --- a/src/plugins/projectexplorer/taskfile.h +++ b/src/plugins/projectexplorer/taskfile.h @@ -20,6 +20,7 @@ public: class TaskFile : public Core::IDocument { + Q_DECLARE_TR_FUNCTIONS(TaskList::Internal::TaskListPlugin) public: TaskFile(QObject *parent); diff --git a/src/plugins/texteditor/highlightersettingspage.cpp b/src/plugins/texteditor/highlightersettingspage.cpp index bee80c1a243..3e1067651a2 100644 --- a/src/plugins/texteditor/highlightersettingspage.cpp +++ b/src/plugins/texteditor/highlightersettingspage.cpp @@ -28,6 +28,7 @@ namespace Internal { class HighlighterSettingsPageWidget : public QWidget { + Q_DECLARE_TR_FUNCTIONS(TextEditor::Internal::HighlighterSettingsPage) public: QLabel *definitionsInfolabel; QPushButton *downloadDefinitions; diff --git a/src/plugins/vcsbase/vcscommand.cpp b/src/plugins/vcsbase/vcscommand.cpp index c62508d57bc..b622fff3173 100644 --- a/src/plugins/vcsbase/vcscommand.cpp +++ b/src/plugins/vcsbase/vcscommand.cpp @@ -94,13 +94,13 @@ QString VcsCommandPrivate::displayName() const if (!m_displayName.isEmpty()) return m_displayName; if (m_jobs.isEmpty()) - return tr("Unknown"); + return VcsCommand::tr("UNKNOWN"); const Job &job = m_jobs.at(0); QString result = job.command.executable().baseName(); if (!result.isEmpty()) result[0] = result.at(0).toTitleCase(); else - result = tr("UNKNOWN"); + result = VcsCommand::tr("UNKNOWN"); if (!job.command.arguments().isEmpty()) result += ' ' + job.command.splitArguments().at(0); return result; From 7c25d1a895e600808f58ec8d808050c2ecb6e78c Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Fri, 14 Oct 2022 16:57:43 +0300 Subject: [PATCH 084/143] Git: Pass only files after -- on Blame It works, but is likely to break at some point. Change-Id: I97952a7cb6cd3569adc694db7537fab807bda0ea Reviewed-by: Jarek Kobus --- src/plugins/git/gitclient.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index 5996d7f4947..d3ffbac050d 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -1354,9 +1354,10 @@ VcsBaseEditorWidget *GitClient::annotate( editor->setWorkingDirectory(workingDir); QStringList arguments = {"blame", "--root"}; - arguments << argWidget->arguments() << "--" << file; + arguments << argWidget->arguments(); if (!revision.isEmpty()) arguments << revision; + arguments << "--" << file; editor->setDefaultLineNumber(lineNumber); vcsExec(workingDir, arguments, editor); return editor; From de357a138e1131e71bea2ab8aa1ad96228fd1290 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Fri, 14 Oct 2022 13:18:55 +0200 Subject: [PATCH 085/143] Doc: Update docs after CMake preferences were moved to separate category ...from tabs in Build & Run and Kits categories. Task-number: QTCREATORBUG-27876 Change-Id: I4f808eaaa32fa978e3ef06d0a3f2a9739d8ef726 Reviewed-by: Cristian Adam --- .../qtcreator-build-run-options-cmake.png | Bin 5000 -> 0 bytes .../qtcreator-build-settings-default.png | Bin 8119 -> 8722 bytes .../images/qtcreator-build-settings-qmake.png | Bin 4362 -> 0 bytes .../images/qtcreator-cmakeexecutable.png | Bin 15539 -> 0 bytes .../images/qtcreator-custom-parser-list.png | Bin 14402 -> 5181 bytes doc/qtcreator/images/qtcreator-kits-cmake.png | Bin 36931 -> 38144 bytes .../qtcreator-preferences-build-run-qmake.png | Bin 0 -> 4401 bytes .../qtcreator-preferences-cmake-general.png | Bin 0 -> 4094 bytes .../qtcreator-preferences-cmake-tools.png | Bin 0 -> 17395 bytes .../images/qtcreator-session-manager.png | Bin 6651 -> 9622 bytes .../creator-projects-cmake-building.qdoc | 8 ++++---- .../src/cmake/creator-projects-cmake.qdoc | 9 +++++---- .../creator-build-settings-qmake.qdoc | 4 ++-- .../creator-projects-build-systems.qdocinc | 6 +----- .../creator-projects-building.qdoc | 14 ++++++++++---- 15 files changed, 22 insertions(+), 19 deletions(-) delete mode 100644 doc/qtcreator/images/qtcreator-build-run-options-cmake.png delete mode 100644 doc/qtcreator/images/qtcreator-build-settings-qmake.png delete mode 100755 doc/qtcreator/images/qtcreator-cmakeexecutable.png create mode 100644 doc/qtcreator/images/qtcreator-preferences-build-run-qmake.png create mode 100644 doc/qtcreator/images/qtcreator-preferences-cmake-general.png create mode 100644 doc/qtcreator/images/qtcreator-preferences-cmake-tools.png diff --git a/doc/qtcreator/images/qtcreator-build-run-options-cmake.png b/doc/qtcreator/images/qtcreator-build-run-options-cmake.png deleted file mode 100644 index f95f905883a3e2e000b3fe0969572b1af5470886..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5000 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 diff --git a/doc/qtcreator/images/qtcreator-build-settings-default.png b/doc/qtcreator/images/qtcreator-build-settings-default.png index 2f5fb01edff99f4a5c2f9a9df4984cd6a0c58708..82b48e93a7ceb30bd23c08c27890ca7df59f8c64 100644 GIT binary patch literal 8722 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 diff --git a/doc/qtcreator/images/qtcreator-build-settings-qmake.png b/doc/qtcreator/images/qtcreator-build-settings-qmake.png deleted file mode 100644 index 6371c6172508ce6d26b41fc2babeb7a3cd805518..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4362 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 E09YVJF8}}l diff --git a/doc/qtcreator/images/qtcreator-cmakeexecutable.png b/doc/qtcreator/images/qtcreator-cmakeexecutable.png deleted file mode 100755 index 7f49186f27133d8fb97379410142128d17033a14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15539 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 diff --git a/doc/qtcreator/images/qtcreator-custom-parser-list.png b/doc/qtcreator/images/qtcreator-custom-parser-list.png index 5446372e66fc89d525626ca99a126811e867af18..1758b1626d1dcad1368bfbc479c103f47a4d7a50 100644 GIT binary patch literal 5181 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 Mp00i_>zopr00VfNk^lez diff --git a/doc/qtcreator/images/qtcreator-kits-cmake.png b/doc/qtcreator/images/qtcreator-kits-cmake.png index 9ccecb61db1357e17cb7a69d7ad2fa635983b674..404e90ce2714e6993591d37917e51bc7698b860f 100644 GIT binary patch literal 38144 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%0ANxb`2_Gi|)o=gQMB<|QxK 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 P3o1Na{an^LB{Ts5-sjr# literal 36931 zcmeAS@N?(olHy`uVBq!ia0y~yU|!0=z_f{jiGhJ((ew9h3=9*vJY5_^DsH{qd%Iue zWbK08?|-w)JSgbfWt!CQrv3JV-SoVfIc2M7ztP_9$*Y;|k+t`mhmvY|aqWX*g_GPf zK7AB;wJbe`?4;`{-*xvAUc0BK#{SMEkZ{Oh_ z7FK@k@9z)Hk)ooKl7cfMf0l-%JhP9Aj<&WgNLi}xH|NKd<#u1MMd#N(=8Wo#m3oOK1G+K23Jdd$Yo@QdjD-*($G3x@-D|oYCx^IO zU+$myp6k*X|C&d_?s;!GjKFH1O??v0!@!VGQX5`3Z7$pWscBtPWOi;;KJjF0hR^L8 z;gj+W85kI9#P1(1{}_v4)rd1NBuLeM|K+&5w!pT2@AML@xleYdcKsCGXFcoLmcM(S zpWnaT_LSa@`BHty!>r40gETj|)rQBF7GL$dt#+cE zIa)XO;&J)aVN3Jp+aE7yWMFW+U4Hx8iFc*>cFtM5PsSNOU089k<$c$KTUG%-8h_*@ zUSD=C>-67ai60N0obmL>y@h*h|FnHx7!|QRO=$AkkJa+g%~Pr#Y?`;`it+M}-!G=F z{MF(8^nLgFOZ+`0%10&cmujuO9r`49pQ~T>t1h@ja{`SCG^ySTM~D&B5#?W z=1)=o_oMo#^x{)x-dE=r*>EHzQ5?HivDv74Ge_!v9 zk9y@8wW<48%G^afp4V5sSyQ8Uy1ct?^E1m8Wnb63UVC=>wZIzBiR&vQKg8bUpQ?E{ zWAXm0XMP&oh$-XLk_FaLU;TWviGijP@eVrvi1vhRKP%Q5Nv+qa)<+Ge~@6PDZ; z_#$t6_N|=PZx5DNe_LL)zo)u4_geYfO^55fO>QlEP}BBm){$R#-pr0T6>uwJ@6{^@ z)|yYsz4mik)3FZcY=V_#LVsW#4bV{d@P3ueV#lZTXr7K~Xn5@~Z#u z_Ow)4`Z-KV+xFJitep}wHVf9&>e_3lskhXY|E_L-b~QJ?`+WL`J+G2aNB*kX8}RJv^pQtBRS#V{QkR)w>C{UpnvYziI@aC=fB+Zt9ERj z`n7pmb>W;?`TON_RTp1hUHZCf-<|$58Ww+M@0;qq^yP1v8Q=eOE#=wQdvL9od}i2G zjay5Lr(NFIy#4-RP;jc!sQlctH;fk%0m(mAg9OMW*q+Q-enV9>j7_3>lJ z{Hs6Sbn`Sg462T&H5$iFWef9%klWqVvf&Sluw{XVJvhaN~r!~1V9cE|rXByMNT z>S65arM7b8eZ?>zgZYSW!7Srn(v>U zo^Jkr_k8`o&ztV828lA975sL0;hA(Wd&S{5Z{9?8uG|)G19k+1MEHH%$%)J6YgOtp zgK`9@5CW@&a3mJJuQP>6G;n^2HN4wzbg%Y%ET~*!U{E_%KK0G4Q{A9+%Wy#D$GhwM zzHRNdaj68ALJSNuSnj{Q{x$1AI|Bp50T$gO7oM z;aK-C|0i6()=A9pxb*4Mr)$@)nSJ4X8TazZp6%^#cK0k|W?(q5gmcfZ$mBKLjS+WWOXpH62t+jrZ`YOdcUqi?F7neX2h_%Jds9Ps#^ zcT{+1(!SjrHx}l}?EJXnZe7h{tJEVC)~$QIyLsmR*|&0c=St~=;)`MNyWLm)pGf)N z7j1uA(j?aXUhZspy!MZ?{YUfai}&BXw?A#!lzdRIy0!fBf712y-LGH2Zccpq%W?Ny zmABsxetqCxwrRdml1F&|jQ4TIs^R;mgtN;3U#5CI_14x$OBGLOt&Cw~U|?7fdEfTa z*Q~{FcJDm+?&SM7r;=4ymTXkI>VL5O_3ykFN3$2&$o^0LX0<)qd$y!18v{eb#80tX zf4rFZ`rS<9ogbEIep)s8%}n`Unv1(jzH36Rl%9hIgcgr7C4BYhzxBurG99NvYH_Y$-+Q+*+^cWZz9_+S#z0NXi#wwkCx8?7L zGH;3O`laH#=K1cM4F;DbK#r~77yZ0%t>OK(VoM%w+&%xE&gz=!ceS&(|5^XTVvf0e_N@2UJ_J=*438Uo-q$UYUcc|xt91L{ zH)p)>vMH2)d{7~_7o1FGj+WbppG-IY5L+V-N*@iGU+!ML8oJDppY7$Vhri!F2gTB{ zuH;|qboOo7uwloJj-GGQ@YKc$OKsJko_H3n`T6tb)TvWLUr1ltd+Fq*tCt=+f4duQ z_gBkNfW=YZh{RFD+VB~(XV0EF^Pzrwo$}lm-QAa;?mj=y_V@g$f1arSZ#_I)ful*G z!>vZ#tzGj|tl_Qly!-1oP9AvZ3o?LZU-$b*yT2y=z4-Zg|Fs{dB%UTn3$i#0{LtIi z{a!3A#U}07$-RqrT`_!^(*FHt@7ILJ@AF@~U!&6ZzTJ5( zYyO{`u^+(-+1! zDR3~|KU%*0ZuwkAhw6yH{zJ3$ruM4t+pc#jy}xRf^7K|aw)P3W%_iCj;t{vY--=1j zte&)A=V{!p^KOb~`{kzYd%do9?HRS$iF*xrEMzL%D(TYt8SOg-tnVVCXc zJ4^F9n-n;}ZL8%6%efDJ%FzkEp`)co#`QJei&g^919{d%|I`||XK8K193AO8`3^pAGy z$5@aR-*0?Zp6s+E{szOlDLS#e@4p={f9tt-?xBX>naf<;CM?}4tm2i*!}sFbo82E9 zA3m$yzTx(vq_Z|Z*;~IpQ$4*P)Bozam%BH9yH!6yM0CTp$mh?`*NNR0_@U=0z_PEJ z=e{VjuxC_U+`U`3Znf=GysJHT&UdqYv)k^(ZU6OexBb75{#7&13w&L8sp@q6pRXRz zFV>h_&0n~B^3mnICMNR}-(EhJklQ-_c`)}9{Vo5F=`S%}{{Pyn^8(rHSDuIq{~;}S zUzMXtVb!y&wB>koF}q> zd3xE`b#jbX_b(J@6PtB@LD)py4Pljgqc+}qaD<`s>h#P-erCy~joF8^K9_q&h6nv# zbz5Wc?nBpC&zx+vYW>p9-hE%)Ry<$4?{!|sy2*MRpfEIlxBHRL?AfzVyhxa^ZW`b5 z^0gMV&)#&4>lfXx{rE^c{rue5?{*)wj$0dT&KX^-HtE*p(yOJh7hhjpvr=&4Y?X@e z%`cgpxAJx^2%l1Ze!bq$)x0<67z;?}mUf?wEjv3;agwQ4fxXdoySnPNg7;6x3skMbGMn&{kPv9H$Uggbm=NMz8nRPT>1U3d3n{f2bbA@=3e=tSy@}~_l*juh!Xqw=H}*CzbBsLJ#DcElx&-3Sls8`xz_7+ zjD@oBBb~nrhLg|OEPip~$GX17_W58paMWyfzaQFUS`!rQ`|XcQ;dSF`&*eYXoCxJ% zO|N`%Sjm59#l|Zcim|=#`A(MaoPLNUR?S0z#qq)J$8UG)nfuNUR+^P^uRkuC zXZ3iu@F$zZWaVn}%C&EUj&E3P`r2)y^iovZdGxqU9{>Q~ENZ+>QX{pY^W;!=C=-)6g9b63xoZs!sPSvvXQ z?xz-SFV>cSIac!VuHBDyU$5M(I(2)_`(Nk7FZc6u|IWMGe2~j=O~t;b)VjsG?fz5t zEX$v7uP?VN(MG^R_d>y)O@|&nU%gU&>C{%1*JA7ZZ;GAH4qnY$;yUZ?hYg<=UH}LG zzUmlIy~DV9_k6#gjH&t2o|h&q`Sx$!9vyp+&nH$Z?TtBe&Un_7NnP*Fp6@;8Fkf-H*P`FUM^kuY2|Q1A$GM6Vh1UFlGN_%W&dGi)D8$X)KccjH+D`BLLZHI*UBs`vm3y+h znjUWb)qEzCd0)2v8oQ(A>Nm@|mwwJU^I>htflJ;c^9*u&W0u`KBmavxpL<8_&R2P# zB^Rv|`*N2nu&*yPy*s1a=d*}0$C-smo~Q1KJ>EKRhFh5U`sfu8-#u7oy59NwHdB+l zl4`?_b2V&D3LJ|R?~7Jiu`)aElDhx4|NXk%pWWVzHl^NJYy8pVMbOSak*hbbmsEed z`{O{t=Y7#PauYYE&HK}QsOs~i)C*gdXMKOPo3VY{e+5mggxt;N&d>j=vrX{>xR3+& zQIDT1fBJ3L)$jNJe(bNmbhxH%1^?7*(f=N;cem<)w)^5wp4hoMcDV}{{^ZGCmJwD_ zX}8tkm(%O~KTcM~zw+PygHPf+j(f4sZ#$K7`R;^R}rZR0AJ zEmL}7$*FaFveQnZ-BscJ4X^HB;AZANn;x+GgjmGtJ#lLz@(Yq0cD*`n5~x0VUb04eW$o46DHmfOzP@_KQ}*ihOFw@}@76o}_UDTCyQCMrUcwEocY1$gRPqHi z*SjvKpW9dQQR(|#as9f&Z+CXS+b6x}@bz7#lQ)>AKU!lO9UgmKbZ^|*SyF;KJ)6o+ zXX_b+++HA7?OOT$&#OuMZ#A5?WIm^{^{U=$yQG%>b z6>OTfKReIyyFTN(aAl6B1LghCcB}i(6FEL>{`~mJ$jDZ3*{&0-E8F+JjlH~D;?djk zD}TPuk-rHl5RznSpp6Pk##%S4ol#}W%k2)IaLt_c@-~ zE=jwG*p}&C;rjkDZK3+=pS_p(k9LSWE#cGh5nyrTIb5zEv*W`t>HIyMwQg3CyQJ>t z9xB(>UA=tI*`MqF*O>mjv0=meFk6W=6V7!#Umg0){8rHq7uzM~wZHf5DlOACyJvl7 z{)NDhQeABadxgWAUbZHFwvrLcQ~DL^woh+Y1UcAcer-69Pe@K>Wo34Dw)C&IlJzA; z;ZKj2SAU63e{ez0?ds#{&pgu2KK;D6tL{U@kDpm9LG29V`O)8BC&L;V*Q*1Mfm#~= zAMfl?@9$CnlJy0*1`F7 zX!-2(0;YQwybtRrDp_L{opRUou4%DI%q-#g&o_ig6kFbz{WQ?|-1;4H(z8kyUeEY* zHF4cUaKj_%!9MNg-N=>Z?+3fTExG5Hc%W^9sHD@gSAG*TrW#r-xV`!PG4{6E-|s%E z%wIY0*R|I_GA0J`?O3($M5+B;FGYDz)=%}>W~*wVFD?ZaqlPMw+VHC4_Umi+{eRbA z|7Wt?Gh-LsRa@?T60f(_{Fbp#dhYoPSD&0(xvqBh`3=in{ePLaPkQQ)HB3)a?re2$ zf4{c+`mvqC3Rc(7-2M7MCc9?30w~FUy!*0l-z{uyiqGs3-CG#kZdz@1^0WNE3-`p5fN+Mf-MdsC)e` z{GHgxrCV8R%C&04dEb3AICA^_il4Ks?azWz>=B0A?R|E~CycULpldHg{Q)Ot#C zI#xb)Et`8{^{2aErtaBMb2c)?3o|3OUy+h2KG6~0b1U%%(?x2cEz>Ba7@ z{e5fcm#dGyy|unQ-Rb5XajWOE!}IRli__VB|IWRAVPbElnuRP`{qe5iPrdI!GZvg? z0lBfJT&LE&{Mv2v%ikx}{XX~BM#cF``Pn(w^=2O3mEU{m!`+SFK}A{P?!(tgRwmyF z{cvvUL{LHYczYp-&0(FrpPjc<#r@mke`WFGGe>*hZF=wgXVupe=8vcCI##SV$E@l* z+qYN9?VD%2k=r-NKg9lgA;QD*GOK1g=l!?899rsrflIXm+d!q7-x>jz>Vscq=C!vU ze8u`XeYIW9r#m%s-WR{$dGP!ECEqeF*8N;#x_HuezRlNPoh(RIY)s#O^wr|1#H!q^ zt&Qt0^uB#5cnVZvgFA=8578Sh;%_&*+s=Kydg(6-87DK|OKTcp6LRl=jo$L@#ZGg9 z=+k%K+g7bZX~0}M&bnNmhndfP-a!qmZJc)$)3dK`GOO1OOJ19&FCQFpbvxL)=om&o$x8CtmrKZZowC2`w+UKI{6z>f2dsF1|ggVD(uN)FW1_`Tq5K ze0)SiL`;l}+wb0zO(k1PEcV@IzaLxvy!W0q%jpf#>5nomEG_!EIV_PM+M#mj{^GDq zZsxnxJh!~2;3cO2|Cii>KrN4NU@-dTn<4*?L{`XRg zA|F2ed4=gE_uBm*xnG`nxqs_(M~Pfxvh6>H11s@_?jN5^Eo<65QYgteclwkGX8 z*va^6mHuUyxj9$&MXT5Aq{e!_U7feM=N(u4`ZT{+8oJl^$LX%wzwz~@JM;Q~t=p}* z%WoGb*MGR{b-(puqEzkoFEcmm$L%>$`~7Hl{2bfzb-UlQz5lQ^@2*PD*_jsE=dXoc zFLhlXzImCOOWvdhv1gZY^ISDsadhLNq*dbQL^NFf~D!u+!XkFX>x4W+er@z+BFW&dsKHk^fOy|w+ zP<}RTmW%ar?;j>EF!{R0?78BBm#g?811$A%HYJBE6biEDw>_RF%4Z*cHah&n&#Z&L zUWd(yn;n(=WM_6|a&=XU5x7P7?Zxiri3$&P`~F^M%#y*s_|-3+7H+23e0yZ-JU>+Ubw`sJ>h`C;QNTXi1B?MwLg;J$mm*ZtD#`@+&D zuR9TU!`1bAs)*=qmUjVLwq`}n-Y>R#W7zienY+K}f^B@X`^P(zA9)K)=JiVMo5~ka z)p<_1ZSBO+f6boVPx@ z_Wxb^|3B1id2-!Q%8K1-`Njgf{QTBtO$_fdU8$V5@s`Eqm8_S%kC!qdXvzY+}iD_xbCu<$2O zmgr5U?5WpRi@xUd;|U0_ovHRJW%ak%bUwEWzRBT_*CO?p_LQ=&59fZJ|7MrTcGR(% za*KV{8^XDs#9i{_>oT9Y^4XO4q09OvAHL>)+}+oI>!;;Ju!QZ}9% znyU2p@;k9Dw?tn=gL`vp-|o)(sCH8IlaJP}>SbZGDmU?;U*Uh*TmwwS1Gply&Fc)is}AYmCct=wp_X5mHg*x)yHcEVXuXz8QDJDeeh4;6|2>U z%R863gxT6J{8SeQE~|Vty`LAzyuC_h`xGx{^Y2$a5OegI z_&$vLlHkp|`P;vj{}oY=jVtS!5_U=R|9h3S>rz+cEc~ODUQn7^ww!5sZ~reBy_L@2 z?oPk|lhF}0V06UdXvNRGcguf1Y5MYycNydI`AhVJ--j3r^f;X<`OIv;e`8sBsax54 zCfBs(kACFEKl=akhyMS+^Z)G^{aD>ze-bi~?4rC+TYhd6xaJm9bgO>)|6ic12OA$` z_z2wFduzMzYk2+l)A9d4z5kyzdwoAA_ck5Sc$oA@q^`N|k9Yk4uIu|&eVx5Q^zY00 zOH==@zi$_|`;${qNZ`gvXU}NXe`{VB`gU8#MdNqwm4W;JJiTxGd*a-m@^*j17KHzQ zcQgLq%%7PXU;SAd+q?f?u=>&H)^PddvFF%Ky)_yW)LQ(dFu$FQ>nsrFtapdi>(V3G3dwiCujA>tcI-{;E|! zU)ukP4V?94;`LOs&EC0Fb!X&WPd&Huag|MC94ji zwc-2EmK+y}uZ&HsyB%h^%}YxD%Q>&0iYqZ@kZ0@J<>upg5!E ze(OcX`?k~G&r;rWievHNEuXUMYF?Q93EpRU;e{{5i@W8!E8~t@WMoX-wQsh*^o(x; z6JD;FQ~%=w`^`;#f8A5fKd3wE-tRRri}3rV^!ifA`&T89f|RwUTm#y$D(C;d==x8d zi}P=tWprA)cK@05^}mv5x_`NQqo&Pj*43rY7ILoqr?j>9`oy$!wRLli<&FOTt`FE7 zxOj5+v~MqV-(6z={?((e@y}-7EsTd4ezg4K-HSE*ZrRuWxnFdAs`ys1m2#I>@0)Y_ zO4#G$-{YPex3pRQ&A#HKD!o?Y?|b))m0SA6!}Nn*S8bKrm%Z{&zt`rHJaa*38-!ocYC)zcNgvY_`~+)33T;9g;q=`&HlTs~k-V$MoW=UaIaC z$lAvCLvNpT*S^(k<<-)UWWBD`%whDm1vT?J+?-v$8PETD=DF6-9JYPk@B3cVv~BFX zrL_9E;Kn#mtV%H6pZa^Er{W#`HTOXS5|4L-QkbIv^Ve8IgNaL?K7G1&?b=+(R90m^ z|IPCFpI=r(lq8EB|AK8+0Mg6*m#%96^JM))NH_1%50shMf*NyPv}rAu;CE~0cKpBeocDvK12ulHJCSN>CNkA0*x~By$I&Y{ zzUo=d|H()?hIOCyu6?h0xTY~r+4=BgV8rEhn=T&xel0kB^D)P>g{Pb}nw?K93!Wvq zf%{5TdU{!VzPB=uMBJatZ{F_xGQZcUTi#Zy_Il` zv9|1Z$@dsUtJ|~R=gfXj3Gft?+r@I* zlcJ|}*lWCBhrHjvFuwjrqSoS>ljKxflLDv3Ez&rwYxHb)rPstf{U;&|uS-uZsVR@C zxjyg8ZB3pVGs2@%I4WmbJ1&}X;z2{+`F+`83$|Un_UZ1SJnnAO@VWH?5&8Sp{K!}{ z>EOlJR{LI`S!(wBy?IRkEwMj;Zk|uN+xH$+E&KjnC$vU+^K_9#St?WVi{6{W|LC0A zCdU1w`Do-s*RFGmH@z^OYq-^7eap+;*Q)kL&3r8j8a2ujEULcy>i7G9KaShKOtf>p zGQsP$?sJA08_zHPnO9=xY<{2DN5$e=O`GZEh1%l&%f&oSZ~b@dnemOw^}Elfzp#2C zm$_sAN${|2(v#Zjm*PIU-*?>oYu)ESh0R)bkDZa!IaniT^4R#tyA6-aF}sIdH|NjH z-@E#_%h&as3v;jq7%U$>UO_R5Mcot;qP_oK?R zn9F^A_W5IZe`gz1{FV6{yZ!cVSm!YD=Q=58-l`P;ebGIie!0Dl%=h0XeWvq0pSj&En^~ey{tt`slpOggYuZXJs7ufnq94%gn_ zx^4Q^bpigM2>J2uV(s>*3A;A$zV8?HtF*fQdx`ek-V2{Ltu*;+bv|C_>68Gqsg**n z|7KSAe(O48^tEbll;vr*>=%3NV&dWxFV-o~g^cSjEBton=hxn|@&9|v_kW&y|L4E& z^$MEy|GW49xV`^x^ygcq(AHVsdz^Cv%O2Xq9`cv;zOTDJ{s{ZIRBZAEqd%!!MQj-0qa{@$}28@@j<_tZQ-^|bqsS5wX$z2EDt3mR*2 ze6YLWeP7S}s>3VpT|WBec~R-d*xTQF&b+q0xzKQ1maWa2nuOb*-QUaJYTlH1U-Y3~ z)O5R-;rE{8o{ZmdKB#Z&>Hi+KAwNHK{=BhSFV-CtU7%3|Wkb%bS9XJ@%8bOn+~r!x zv+hJ)%0^RWQV> zF@wuRrhV2$k9XVt&g=cT_3W>8*6il9%cGwjzxQaT((=ukN8*?A{oH@8=8hk~uI~M? z5FT(c_z-)b+`Z1uIXqK4ee1VWJLlqkv#&3lQ?>isiw}Fhe)_Z{POc>9?48)}*E+z( z4#>U9C3|P>TX9-`V(9gpmHvNo+TOCR3txREzTD1v`)|t`(~qA_x%=Gf>}T^(@h2;V z_^|GM5{}JDd*gDz9#4L{yU5;1v*mZ|SMcJLn(d7DYme@epJ}OYwx4mG zssHsE%mP=E)@!`4ONsr|u3}NL_f}NF919csgF;os@AAKD$A19mK?2|$-9o}{@RY@d;K{si}zHWt1bD^aU?J=eWLAx9eWHvWPG=|t|P?a=(FSf zHNzz{!2NpBb@HG-{rP#iaphJg@7*+bx%?tO^UTjlXFqX&o7CrZ%R9-{O0<~UQfuR) zRa!MMpz-arn)0)|3g5adJ-_nz%dk7g7S=S(oVx$fPwuq3V%NPIph2K_zBTiu`zM@` zYhQNz5jYnIwO#bC`(IMMZ&sa->+wzZeDtq2YbC8VkjqHFBYk6Q`tp`lz6*BW-MO_O zYT25m*_Q=b93SldT6_KCz9JU0YW+#8+EV!U6)rfQe%Zg)TsP=Wkk|dMD%a0u?7N*?7RmrG0ik_`D`^=8#g5sO^%!bYn*?y_WX_h&B` z?Kf9X#pLUQCY_IekIk3Z`eD_Z>EHYQPuQThFZ=eqiIscRtxb-F6&rq?xBlC`g|-Qj z-|l`%JXHBQNPmiXW%MWUYf7STKiQu$`EoZVx_9BbSL=ek=BDoczM^7H;j6{{G1lh) zaO zFLqD+_H5Uk($c>7$HJByufCM~=hqP}8DmhRV1s>$UCM%pF%udir49)+|tI-*miu|F5g-tu|GE`4}57UmNV>y5Y}` z)eU8ZM}pRChlL+6?+%Q+pJDs@?aA_H%a3;-_Pqc0X1D3H>t?TazmvIhNih3t`iZzF zT`@lV6Re*6j|z^pw%u}N&NP9Q5xVzv-&`}yx3%3~aCXoA9;@rK!dAb!)BWDc=6najy-Qqy#BIZX}4eC%(;_P zTvbJGDyL|v3l*NsEuWlo^)zePkfZRPRD9a znqI`7e-D~F-bxQ^S*D!+ef?8P82uq z|Ks=Z&I5J-c^;?k|2cdA%bz`*&$7Q+CYh&7AARr-)Z_e-cL!-6DeUO%{Il=h#O*JC z|F3-i@9O#=?e!7q6N`K%r)PiFtk?%yl4ba9pLVytj*gC=p3>sKpala_Rx;q~m!s*$ z&GKSUSjru{yaP^EDSQ}51Mr6zX@8N&7d}80n=HOfs|(OLSTjl#isY5 z<)xt2*ASYU0kp;wBKTl8h{FJ0p3T4jUg^sKS)t8%Vt|>EYo~TIMeD2!ZwRCTtN{VBoO$6Au872Q8XoF|Je+d*# z;+qG!wi*X0wrok0p*9gjdUMc?9=Xkyc^Uw0oAsKmmcqa&in6)|Gy{oVV{oI z|2dvkcdYzl>;X_p<4BrQ6W;Ufu9ph)zV7#jCe)Pwy$D*J>h?7{?d`?SeFYWuF8kTv z%l4Ig?UP)*Hy^a#<2rc2Ub5rdKR4A$2hZqtKi*yY{chRv%lGc<-h1kSZ(4cX>ip;L*Q)jJc-^UZzf*4s|2*}l)qCgWb02OF zHavIQ#4Pw$S43#>WWA~v|Fr)#9kdJi8#Zlc+FOx%v!-WX{F`}gThe`pR!-3Rm%D}^ z?v_o`IC->O{aE?brj)!8@59u5r!QEi#a+Oww3q8e$X9lqVnF(mLU)w$0h~wc!hRsSkU-1F~8-oM76h|1M*Oa5yIn@u}f?tZqMd+Vn)CqB$8d2qqolxEUERlbFl#Cg?>ni((@eR`SB3v72`kzle`+e*ZU55u{f^v?zmNPhy`R6&eu>np-aDYtgU`|8yDCH1zS6t;cJGh8io7kx z#Zj<<1KWo8LM(<~_pLV3Ti*A6*3Z<)$zHit zetrf+mCOBJKg$=d+@7BQ{;p$oiLyS&d#?ysJypL?%w{JEQvIW9ctwdCf&y&t}2`Mo|=FLC79 zJmu?4cShU1+MUN=GPeZO)_S=wdST-J|G)Row_dlWrV#!al z&0T7*&;Hr)zOUo`tNDxG-oMuSyF&6Z+Z^trUwrvL$=^PFeRX88@~U_4X<7^PHav3U zT9q{I`m3!a`gcs7-iOz1mS_4bxyOz(c72)7{jJY$aJ=M?$5pk z?K*ZMG!?vv&hY5Y*ufP;FY-T@0qdc16$Ij`5=oNzr5S+X!rl;`Tqyy{~UCe{N!}+(e595J*&VS zJeGaYzuslsc-)9GSPC?S~$Z)KBIq?slMY8yEW|sd>M;-yf2g@J_x) z6{O5X-pOy%47PpTpd<|HQZ&4W);8dc3kddY|ND2rvtl6EBVfJ_zhXY1ln&T~o6NHHG2hN@lN`K$ZQJ6l z@0@Vv#QstE5FWfsgFi>Y|rYkrS~fuW&og7E4&afLiJ;%=^GPdCIsHaLJbFC{B453$_1XwMJ5 z9(<3Vty~RvNli*Yd2#V%eKiJ#hO~*7S3}(GcBuSmy7<1S3=9mjT>aLA zT5ZZE`>N#_7#O5lKgQOGgWG=~N}>ZiEnv;Z{d#-eTKzf93=9mtbK*YUHT-tB%zg5f zEd6b}@8`XL|82`MWgVvDA7c%#?~dOYeeB$&z*=!;HqeH-`?C4>L;1AqZcYC5jccE^ z0CcQJaW?(7UpTk-TV z0|SHdnmvv;kCv;SE-#+0`)?~xg!x)b;J8>A7i%~ z&xNiPv|7BQ=Iyz2Ngvlwbo{3qEVllwPF1Iy<)$}xv%4#At+l?cw_Ltc`1$Jy5qmvx zMg|56`Df3cyB6>N`u6>a{hzF6xcjaD@h;`(I=KkbW6|FKb5x&ptpD@gBe&q-hnluU zJ0978UtGhnqj<~8qd)SdzF*cB#dG}G;Vm;6@9#~%wf5&Vo#Q$N;z`+}LFaUfYZ(|A zl9%j>|Nm?6j5m*VPqvt^Xmc?vXmet?=@qFjce@^j|Gs>t#JiL4m!9%Y_fN5VjrUYO zeY<`A3uoi{*D6_J+w<@51T8AmIlNTcJU?H5*V7rNPrWw!JbPEm?C>923=9pPE@0RG z$TOULpyDBCe$b1&8awA2m1`$w^RnGuntAswuh`L=w#C2mR&BmL{fzjn=Iz}vn-2H} z-mO{beY?DCY4O&rTobqWbes*1*;V#Cw?v$Q;X|y1^WLla`-*?hRk{~f`#415Z}+5{%+)os z_f7WSKjXRY<>8BNm6nHtYP^XLzFv=?zB@%LDCk%1vWVH>E<2KVE9c7O|p z*-T4j{?O~`dT&;F{jyUbqNUHf6nI%dOu+?8bUVs|AoE`y#>a1#Pu;%W|Nd&n&z%|zi)d(`@B$5lMi2)`OQrA zR%_iKXnBZ$i7*nqQEA=EcB}@F8YTMWD&|#r<}Z>YtYSZ@a$x{k)Uy zDP5m8SWUD%Y;sO>y`P-1^G>!J^LM+edalj57QHG*ThqPfX??m_eu1Ne;QdlQp0CH3 z7Hb^qQ~iHgs^0F^`E0pqd8)f*ZUd4!m!5=jpsUHRa~rSFHR|)m=NUet5S}+Fq>eyYT1RPxsCHkZhtKzo=;M zzw06l3_i=>OI>$L{hrV9bKU&sTPit2D{oHu>AkzYYfjev(%oLsJ+t4GOuoMP+t;i# z_hK{oIqy&P1--Gbe)eV3lGLRs`EP9|J(2OYwtg9QFR*&i=Wih;wrn%6y`7?e_Rw6_ z(5~6B&%&)_ql>NedtJ#koE>BFVEjKyv%rEr+!Gu4q3=9lE^8BYsOXnUcXS^@l z^l{Oi#QRs94>2$ptlPJH*REf;L=?^*DNhWkDfjBLVqja?2V@cz2P+2UMCIvS8<0jh!;Bhd9`{2_b7@!3-R1n-00u|SwqLkr&>p?}()}W-~ z_Yw>Y4a>mIv*ka}MBiopp~n+dmY$YoUs}|s0BR+&Is2`5d^p)9a3f^>$Xl?x&O~>; zKlEXu{r))E5)u=+gU>m=AAzQb+TI>=QRbbJ##YU6ztwRvO0VId@%v}bp0#Q^%#wV= z2x^;whQemlK#FKk2OP8_4YVm5!ai~YyrW~Mro*gGcsn*v)m(17^{nOmAMQ5(eD`kO zt6$&#<;Cmxgk4;}h!H$&*pc|_9ccgktQh-@?fmP0uRqS1`Ap(>-Xh)YQ&n_ppfkLS z-tXHzy=2l}o2LpB1Klepr)sb%p59*h&&1Z-Gk&RliQC+RHT~~*?R)K!XLT=HZEKPA zHRl7L^d?{bn{#-}nuiaI6wQxDSyi|Hc?5238!GNwZL-e#mRGT%ZqTH?f06?qJSu&+ zJ<{t}yw=V`anszVY1^N>ce-0CdSgmh!DA2O<2TBqJ5^V^2K_UuPvj`DVw&@Ewc?Ss ztIO8^5vc}mW(E!aC1urypUn7o%l~599Pa#CQ@nD`HdL$qH#dIV_i~5qWFy)8yxHq7 z-3p4`%<|eeJWhAw*_<1miFVBU!0q`T>#Tpw;yqRN`R z-QAZQGMAZ%-EV!UY^`+P9o+Bk=eTw6LQu5;F%D!gZ^>y`!k)LZc-I8 zH;GkW^>BZ~hx7_x;lqVk(o`3sQ>}QXkb5s1+--?_# zQRK&#~SHji3?~JwXyfoREnY6lV%KUZl@}UR!F`_k> z{QNAxKLiZ{v4E$nE;Kp6Jp1F_!Fh$h{P;GRN6CG=l2QEWuE|G*+WB+$ao>KmB6y$Q ziRq=rFBVz7<+tDRdDpp3)0gO<`W|+2nfNbBtEbhcyLatO$=?^9dT`g?qxXe={(iLi z3$yZF55rqezdSD8eS7t*pxmxki@S2t_ugWeJ$rh>l*i9GGv>v&Cvbjzm3i0qyXs-} z!=mTP7e&<@N9>iol~s1lZ_l5kr)U4ZWni@cRlohZ!U*5O++jZlgD}VT; zNi2IV@^a_qITvSIzsj}S5~_AuTRGo~??w8B)z**qg{6pI{J;9t4ApS)N%}9B+Pl z3Uu`1_GrxdebHV1B)A`Wt`^jfU;J=)Z)UFlzUVo-bdO$P6T8IwymYz1oxaCeeF|k? zV|8WI{+*ii`TDsV(XMB0JxdLK9rj;8C1LsAw7X}-*Dc(}zc2Co&aF|m-@l$!b;4}z zq~w1~#dTD5SKO}Lw&VS=e+LR)2c({<(`KqaDslVh>J5LVJFhvp(RO*#YS#ZonyHo> zE{EhhGGzEw%G=dQOa`4I(kiI*@=I(*s`!=PxAQC;m1LdHwN3jh@;SbEM&#boxYvm< z|77i6B6>zZ`{079TfRsCe!Is;+keTEJ@Z!GZ!-0tzV^)xk5r|X3orfoHmf#!UxD=1 zd;8_zf9~29S{t>~=Zo{=*sysAm?oKKrv7i)v}M=3GdVxQZ@E{RrqeS7(&OJA8j-Z7rPH$Vv@adcTSPvmP@6Uw!hb9P5sH< zcKhV9b18MN-X*)4p314Rp5nCHM(UFNy8v%XEBpENi7qYVc{hORbXgqo$tv{9;KF=VQOhyoI}*%Qwv}J-#k= z4__T;o%Whq<#?0r(V-4Ed;MB_ZB_3yi&ppjcM9Ei=k=;n*Q3wo*fa15Lo&4w_q6wi z9(4ZJzVdR9;bixvFQBCnXfp4L$-Tu|`KNdDY&;*W6sssL4QgF|jFo7E^{x$kPweEs zf3!R?YR25TvEa>YPj>S&Ff3yM72W>l8ZsF6aR(nfIaxh*`dr8`E-M4WjFKlc>gl$Y z-%m;kfQtR78Q>#iKm)%9vL_<>Z$g`JkX9RLM>nV+0+0=jP@zGcfpA744h6Ij-*4>~oisO8Fn` zHfTH%U*YZT{b|ok(4^?)#yMY~+!3#t_hHH$G3_db`>mWVWe3lm_1*WIfgxelgyQ*g z7?=OtA_3}Tt6eUCTF$%g2Sg&i_5Hm0Odn!r?D}~3GXn#|WtXqep!OMQfF0=oQIo*5w?YGC$kH6ZQ^p(o_nah^GUwQxV%eYg= z7-opwe*JLEj9TWI@s5wb<{JJ39p%)uC{HikJU3U{QD|7pAJm;VHYUvrP&@J{McWe9&reD!qrLp+WBA)F&i)TDo z^~Wk?2}{((Y4uO;nN{zI>D5 zZ*PwN>jQEh7HU5&pJcl?E8W#C=)q;V@D%-d>mt|wdUJEH%l>syu_}ALGkLG>3@cxD zYT2|~-c}oDNyO^!{3H6(?1{Ye*1(^o^Bx6nwJSa>T`$o~pKYZQCkVY&&SYD&U9ZEZE-@;5d*Z}&Jq_o&6W zs}nP2mb$7zD#L=ldk-eY$h|!Jf0a?%$*Zj^^rejN=DGjTUK3ZfV@pDN3itB)Q}nx@ zO=*3`9a;OX_GaYWxLD`E;;rG?pI>kL5*7AC- zzwW|&eDnQ!B%ROoWv)A%E6mWNC$1kSa`x|w#r;p>EALKz`}N)Kqu)F1{8#3?r)M1d z`RUcH<7XD^U-aSboe4D?4d1GZ#})6nR+Cs6wCuq5-V)m#(S84o&M&;bPx@V4vDbRb zdvf0IjpiTx9Q*zP@4uT;!e~7k}Y!SH7-lDU0=6 z)7NSH3V65He&F4Gey;xRSN0zl-+i$(O7-|Y=dVpm->{ZipBMXn_lCSgp6sfgoB6F* zuiX$^|Kja~c#$lbWA|^=O2jZW?9+OGxScPR#5)-^zrkL?2NzuY&E{O zZ}yM2%M0FHr9R$WtM<~waQ?#C?_d2rJ8Q?HW&2#p7}EC_eKYhv`_{g9&LQpBcf4-> zUi)|3ugXKW?44Du?Sos&?}jawy)Ct{S?^EgQ=^Ogy=9S=Ox!8iSzYg!KUjI{r0|bxSNEU4Z5-0O z{?volkG~$M_>uR5Yx#wmwsq>vriTyCI-l@8Re%3%XXoh6KY#Kt-shFy_3hx3o69%% zt6mFyow{OEO1aMN9}Cv>gnp{syWsy;p&yGfHzd11T(Ek@LU`r*ur7brYQt55X_s#= z{L>fal(F`BWM--?lj@JPA6oY$b??^y^76=>tD-L$*W3SmEyr+w?d#pg-mkrxy8fX3 zw!Jpz&ToHL8l1+pZ}s_o-(=S?U0?0$xA)wni?6R*&3(`yzwfsA)80G%YZUKKt=)LT zy!;Ytxu4_{O|4q@>uQx5>Gur3ZTzy=E~`5xkFC7)C)2rKn^V;HM{m-y_hUjsv(r0s zp6uhOt8;_DiGAa`D!Xg)+ZyXK-N&ULygx)IzLh?@=%Vhs{Q=s#-^Gggx!$O{$*pH% zFgSK%r~b3uzqVND^aS63P@Rxd^F1Q3b-kaYa$$nMabbO{)m(E0&Z}9x3_tSZ_h&Au ze*jsQ^5dO}bpHMG+&^n`Zg?;RKi<81_3HJ$jF)`osq+Ls*&SSzz|6oaPOA$5V%bGGRHZU?Uyt;i4G}mVAJnM3MauXmBL|850A;ff5Cz>R)TZ!MmuH>_vte@nuKLpH7#BRj~4)wKyfc@8_{I zFw9Vs01fhh2H6jkGv06Y{CKywx3^gN+@W78#8-8=!}Zz zx6Vd~Ki|CN%iCLjZZZay_kIq~e-~rN&Ad>J>&GjFn@BcgZeukpvq<2rI6oVbw z@#EdUJnfe%y5%+If5Ss|D68mJPTsGvb;spq@#D4OcRs$GYdzEM*{;gU=jXPbo>qIR z+j`RSGtWATK3=?AqNtv(c;V;rpIxVNQg=<9#Jzp#zpTD~hAZW@?1nyOh65|$ zU7R!J{`2*Eu7AAi_j6{|#OS>D`&NGoKl*5^kH&qi`}<43K0Etm?vnWEko@4yKKGY# zZg*+zWm0kf7}z#F*zWx4_&Yln$9c@u`s?%W;<==KdV7z}b=~ZhSy$|O?AOt%wOWh} z2eug4+w0Hzy*9tz{omB<2W^Vp9WMVEE1`J*t6N!b8JABi2x1?DQzisti z&iwQ4_Da90kM?X3i@7t+D#qy2+@!yvGhc3hf9GCYiZ~NPg96l*y7$kj>y^Cvk@xAVaySH=hKHvD9crBG1(@vM%%ze8*?wr-W>X)~2ua}fs7TyoN3ObP! zz9V}0cw-J4M>6&=>{fgwT-JjNb?cVX%?_b!Z`#b;oN!=Eo`}B+Z_U(FA zmZ6R3xRW)tm(TP~{h&Rq?#)#e28IL9khFN&$^5>o&bs|fpiz?_df>%&ppHL-D0s9~ zih<#Pi6Ug+A@d<Y&AAM6NGWtu3X!j2`5c_FAsqDlEfENr%9#b4D*mC@ zqx06!-8ydlA#)MOH}}23=GKU-T`0fpxkv}p{s)h;%&@qB_kOPG+$E`;^Y^@X@6^D_PzE1`Q|_sOQ~KY0p^b>|oxu;p;9H*a>DI_Jo` zg_lD+u5aEY_4C*2;7tz`U!N1*q0hizqOyBY(&^Kuqobp53wN3LO3m0Va=&%rr`Tk^ zyrW;Uj@dN+sWMHsYu+_y?T(Mm1?QUgJv+NU&eyv9L6NEMv)Fav+#!c&=_jraTzcf) z*|zsg3=YmG4m`hf>C&rLuUb3Zq%_V>IreC`!B?J~Qw{d1i+;UJzB7Z7f#Fy4jGNGL zXYiPNmF{-oz4vc={Ll+7XJlBgeqVJR7i6d$luSS+D+4GtZ``mbPl4dAR3Tlv?`mt|fu^YNSAkCi5Z7mW67eBXA*%-dRH{hxEO7MC9-DHxrVKb4$zs#sF9 z*6_v2Pj`K^zY9OEs(zie^ybue#}0FBnsbgf;OkxgyG56FFMpQbeaCfY)y*|#|2FyY zeQt~fCFsMSW0w}jdeoZSY0}eE}ik5c|PFY z$^92Dd|J2kSDD{W?Y|o$RS(@eyzx)blqJh5Pm3Qt%yBB|&!n^MY7zaX4}oSD64umQ zzaC&5xVP+B-d~%8BH3-L)UREBw_EhPp{e|WsV~#DFFs!X&^=f4;o1s|wVw}F8HHK? zcs>104d_@~iTL}xJ@?yc%uTNwX{K#Y^Q@cq&wzE`?0rHHbxXEa-d`KcUbFq)xi6hk z9%jF|7#J1^-e0>u@tXhRmj+2o^;h2C%p8|uV-d2vf&vZ`q^_&vr-MfCa-(2y?oN@1p@2edzzvU}&vai{G z-Ka2+p~3D4Y*<_Peyid4yLOoY2g;Q@dUc)c<@!EUGL-rU{7KR3T-ymRx#(_Xhb zui1WW&;1a~(BPR;R#ujnm{?d?xG6OpmJ1^HN!K=b=(SeGY^l-kO!>1l@;VgkrQ|(u!l>X?*GEX zU~&Z4$oZVw>vR1AeliGtKjinmQ_=myT|Hr~FI!hB&M%Yyy<9)f)cex*iQnX9B&Ot) zFKW52xbXS%8REguxFbbZ*X%m=^3>JPJ4^JpU5cno?rD+H0!6m*j_2$Q3?lZ)pU3$LtvQnj_?y_Krl`tZ;5)&*Vs|BQwIai7Rh=ZH1ZyRM!% zub>aACJkcuaq}8Yv1IlM0ap#n($jyr@Ty4Hwt|+g!T~k)K7p5- z=oD2=o&3gReq}Of<%U2_cwNbZXim^+*H6AgJ>30#IYW)OTXxNN8wLgjo+IBtWiY5| z+Q9i8+_D8t30c_h$Kl92eLgoG3E&hh+Tnf%KHD=$u^uDm_r>_(1`z1Jkoj&Sm<5WAR^ymC% zo!4IVJ?lJvZJOWltdE8~%DOt&IQA+nzO!k1mrCVoJCofWs>@n$SiZWlSu@(`^vSG$ zU%sE3(iOU*ZHA}S=39ZMd>+;+?##&Uv(oP2%Xvm`{ryfAP6h zoEmh~^_T0%(EL3s!w)asvUJm&Ewk>u6OC5%*Q1nYP?e3EI&(g(m z^%(ze=I8sXSMusrUB*!_W7B}O3!>BNORq*{E&G`s_P*pzZ8^(+btU!m=t*6h*6xX% z_3xb4(&I1VmiLzZyI`JBsIuzb=37s~uf1en-+A%9+uqAdW}i8cTb}*1NT2gd(1zck z=f3E(Kla_oA2BzBo_O-3M-)&$-oR zzOnMwZn@Y~?|V$yMihf{fZb>rA- zwohI0@Zl)cfnfCc^4qtuy zY2Ld7$F>?t|E}4Xx;9yD$K>0&Jo3H&+6-1-IX>CSM-j^anB&s@U*_b$|Iiq(++WXi zpnP@Bb(tw=mw&Y?d%c{6*J4IcppnVD-PdOBun`XZvF>wbrGXgx^>+DdR$H3fm%WU0 z^jv%H&F^*fZQVcf)(HIHtvqe{xtmwsPw28Y2>H&oe{IsgtU9SXDWYHd^Ys6&jlMhg z=b~kAr%CTl-nHzvxKmF3>8ne(1biLoeXgHV^eV?XZL^bq>)wVv zxf@;E^TTw_y%f>A5gYe*ne7r^!?tTr@}E9^Gds6u<3b+05C$OHv+x zyK*V-_PXDmio4jEcsgPZ?V4A)d!ELk_>0T6>s`Npp0Qm0e|hQal=K=?)_D2#?8nZP zhniRZefninOi+WELjP{g`pMJ#bKPG3=h4a=9-ir`Vs7PQ+3VQb8&{a|bAHv* zu+(qi>H6uW=YFnTpmWxzUmIi&;5M}h|-PgW}9rC;HH@JVkdx3Gr zj#KhqYD_PCa<1MfeN%Vicf-vAPkkRP$#q`)Wp;*o$lK~=8`N^I8opf8b|?LC?v?ka zl-7C+cCD3(FXeWHYpzcKE>*_nnSkH(6g}>zy7}m$l@H_KH())}K3Y zX5O^az52zwXEv?vEO9-$t4qaty3SAG_4Ce!eb{~7&aLdtpFG~H%l~zMaMKSy^CwLG z<<)B0m^TKMx?QhUO}9SQy4>3D_8O7ZmyI(Et@G9YS1W}?Z*~p+Juj+r)#jf@m)usb zzhv{4-|j8vuzQ-Tcf7$-Td@-+y44r(JCbltbLK`+c}s0P`&-OcgySFjb~+V z?3fs<_g?b3LHV}W6}{Ka3ElF3T%E8Wh~ck;-+IIQSJUq$2G(ivE(NWM`rY(*ccBIA zueZAm;_jO>|J?S9(?7gAf1 zsIjeh4<8t;5qCRUzWm7wW(Ecc*4wjT({FR$JoAQ*`n8>(B2x-l6P;9zypLjTz>-Y( z>JVdIcUjO)AUDwm1{WqSdk8jLpQM372^q|V@5$+LJB3%+bR}(ie*(0m>(OoltxekP zPoY<#z!%(_F58pTxF~6I?X$Dh3i+D*xDUJc9b39owNl~?XpwCOhvmjopq(TCW`?ub zEq{1s_IHO5ou@#XH6-ucy>sVDH?&2ir5^eJjCPutYIlx%_fn4fB23p7-zGE&n_J^1b_4SN`%9Hi=WX^5JglR{gts ze^0f^TdN!L>eeN{nV;X?)nD71o0sXXA2Bz+cGvDFGZv}5(~b-lFtE=U?Cedv3SNY%0#P0Tb7L|9CS3B!<`2sJ;Rz`+pXTWFNTzmKndcjKR z*KKR>_1axJWBBcENZHo5Z9+@;ym(phYhC4)v+;XNpFg`*^YPuMt=8rFacA$HU4Qq= zORkd}iY@moUcU71&TMgsv-Ll3-OZkUdg(gJotHx2$Ly}uUhnbkTUeN+=31XA*Iz1q z4quz|vTC!{i>Yz@rtRIgCwSdL4V4{b>;Hx2=Es>%(tm%4(^U6tS+4q8&mGL?kNp;7 zU~rq*+S#!9>4%6t`SLOoZMwH_tGBs{_XG8WN&A2pS_yA?^;K`dS<5axj1Xd%}2jq)}2z9Rl9;EYWXx( zk?s{L;fEi3?8}x~vNGw>#PZ(@ssFBC2L8X|XCBwN85KSmyp(OPV~lf(?L!^= z;PoX=6YYy54@ZW6TM{ZVgHQ4K%A?PpDXk6K|MBkPU+e5%-P=|5`PtR&YidnJ4<7yC z{&V@MtM|Cxzq!^V`t!8t&h??3kxBn{=@fsA3cI!S^sX64=l)q+Q<7^KeN9IzYVW%2 z?GbyPU5VfB{peQGzB_A``d4kv$eXCp{9>2r#C@y`4Hr}E>f%hkN8kU~_eXnu^0w;} z-lr{^wR+X2p!6lzwp{&Hd4}hy)Q7u^f2{kpVd|8{JIl^*TeCE((=cguYeeda*6W@J zi`t* z-uHFDjjSUxraG?5`4*M8I_vpT$Mc#i`d< zKiuv6bKM(_ z@s{h0E(1f4Wl>e{&vnJmWSMT3TxR@oz{MuNX(!9%7rO&bgSIdgev3`6*#3W;wdKCm z$CEGK>->DT=z^g^0_eJ+;~&5WD1r*j^TKU`AMa{fzG7ftV9Tj?ygzsD+^N66>gnnE zm3>~?c*!SIXCL?B=U=bK&u?4I2U=#nO&wHPgDx8~wA?xI=BZPsUbQIsPG<8mUb^vz z9?$&Gl9Ow%FfuUk-1~MemBoKwadEM+v9Y?EROW-i6!H75&!&d|W2gmn;H8}W))?Mo z1r2#Hz?T2w*p~qsq-6LID`ELWui?{=$spa3?!R^)XvfEicW2JLL2L;;@ly2Z(egx< z3HA=~6XQ(eiY6U30WCm!`R`yjTM#a32*4ft$-89Fo{CfAwH(AlSs$P{JV_}>0%9(1+U*1?3bz-&L z=dJEFlU2O^qg>mTr2fduJbmrZu^`vFY>!CM`Fd+U-;m<_0lHYn5wyKf@MBxrQEOw( zukN{1n^y%s2n&+R{kA@8!EH~acA?%bzZ7tql>e>p5qCP{%(7--B*ati3seg!E$%e|*O#V#KxPYjt7 z{*S>Hlu$rBZ&DyzP8lF$*f$RT~fhmI6+6y`1YMUdkk1TzOLSFZ%4z%zf83-(6F(yz%Qbp|jc3E_=j-u88~b>s|Bm zTLJSxL;WvyudGw`3^=%}@4eZgyg0>`p0B2q+%c`aKKcEsIZKaU3rIQKvwQt%o$XVP zEU|h4+6rl)xo`KreRbR9uJflX(7A~|6YzHT&bG*?7yRq2G+TGQyz-4*e#_lCGnvyY zzYAxtxhgAB>UtqK#P*q1+`GP?Y;6&1nU-!@vUGLewNtACEvGvE_*wsVr*VU<+~Kob zydK3nuHX9o{q8=luRZraFI3u?2D&uw^W6!z(=yq;*uG5>6Mz2Cq+X+crsnz7C0~v% zIDWU>d-LTC;b>a=@n&r-Ax38AZo%8fSmctLG#YatRu50coIM-De zmKL?}xjeaisouw4v)K&%EVPrzFjvDgVw}yD`N7`0DG@uR}NAeK&Cl3Py!{R5qznY$z z6*KdE$eYl~NrwXBo&2h=zqqb^{X*f_yB`i@RsNi|FSmJeQc3MU&#zyz&dpf5%j%5& z*?a1{Vos;VrKgDoJl`gCPNen5gY1N*%agZqE))Hm{AE$)-dWbOvSTc+U;HdNhx-dC zQ#E}2eE0G{+2HD{OI8>h$unzs-^UtU&A?a}VP zqWfRDTlpj<&s+yCI=sX^XAQ=Lx&zcdX#c{ zoe5~GitRGk`KX|L+asXvKTiam_gfvuDnnxdzFn61vZxJv;Km&&TA_ zfyXD_z06VggE++$wCa%IF*|Ii8b*U$03FYU4_O;zorn!gnQjFtctAyx*bUt|8E$8)g! zsXIG-kFh9Zo-lFCvGVhvDRPG#@Z>Kj2xk<4k0&!{^)Oz3wY(gBGwMg(dZF6w8yCNW zlt6(tkCvo?wiVtrwyU`HvL{2Dfg!;tXMMYIe%UXh^YM>MXGzuH&sCLmz2v29T%TAc z_vPF!yIIpS0?RFHzR%bZz1JX)f#Jn+TqDWJZ+6e@%G#c*>Z!f;Oi8uKvYGpGxA#BY-F&M5{Vn&oGG3bs zzn|J(?YSxZe}cFDs#<|%uHm}VuDZ-I33w1E2)spq~phB7Oz(t$Agw5_MSPPlm}goSpB8qcisUFA2;M zPyXFbyX0JVW>jUDgx4O64E<8U^YnUVqyK3sD|ty#b_RwIJ1age%6R84U#s%})a!XU z>&x$-buqP%UnM-vI4FDGiC51JjZ3u7pSb*DccJ##t9$y}z}pMn zTdkiOeLUy^$G+FQQ~cRyS8lmt+x3X;^>y`(C%wP(Hv>ePf!EIIU9vaGi-JXz-YoGZ~izqeiakQZ)ywEXw`-N(*d zka}@!Yx}>~XO8h*U4QybuJYOFTRF3SdTw!IU|=Xcsk-L&%B7D=yTcc1$_u2+%k4F}bvOTJ4@&s9ZQ{c0 zy7Lp?&ONkZUaUyH2_xv1)pdo7*F36YT=>z^{Cwu`e^CeTKl~W$U-|a;#IJj7PjO%S z)AugPCT9J$4e#T27CoJEuy1eSD+ znz-~`N_5Hbbs4U8#lBTLBew0ainQAu!@$s>thoI8jQ3XQ_Y^1mon*O2PD1*AE9bYn zCzTa8>^su_Rf2&bp{VA%d^Tv$-dv5xU)^h&51)H_z=VOp;eSneY3bLk;Js#tR{VGe z89iE@P`#)Sx~=GLP38iVV5#o+hd#`l1a7~(*KOYiN056yT6}XpQKqlfq?E6Q{)h|IxvDmUQ3E5ZgC$C*HdE%BArhOR<4U2z+ zu36ftTnybpWau!<^4Y%Vc|W=*@1OHIhug!Re;%v*hc_oUt64-(-KqV=8?td`)vczV z`)(N?*_wB9Q?UPszND|-ervB?dtbw6!qBkz=ey?RzgGWz$y`(Juy6KUiLJM{OlEd;jIc{3x^EC6C;H%xPn{-sUH_dGV(C%vDlNQ_sB2yK*VN zw|?!^XZEHn<=fs}o4PjHeAD`<-k*6%@47^Pdn&%3`>FS`rRZJ*slV?dR&KuJVajy8 z@ayl5FSr;Q9{$cdk-K>d^Vg-@mv7nehHD?Hgqk zz6JjMs}iw#)!vK~f8(F-jtyTG{AHoM?YiCazRK4`85kBn*uAf9R_Cj&#$~Ucb1u6P zKehVDI?qd-=hJVPx;6G}?z83IJm;r?g#G99Zlce3T0KAX!SeQOQLDH6q$NYFXQf>E z-Mj0bK(f{2qW$yUvzg~j|LA>u!lU1A^0nnhe*N#*m%T-~?7DjQJjS`2ldlYvjH^1At(@!qCcI`>g zh4-`8PV>L`=WfJtHY@dOKLy)gI_|u(skG+${T=_zR_|XwZ`=C7)$`5=951h)v*p(F zfBjdUt@tb{^g5!q=SmdwfyI|a85o+s-TftX&2`tVm(%_pz2Uo>>HgJ)*SDTDEs3f9 zzNOxO`HvN=b`?ez9^9+T`%N|JUge2BuOoBHTrLY@Q>%SBCw&VKhm&sRuI3KoM z_m(-gWET@dLP_oR?vm2$y3fNHr$)8D=ev9G$49|cW&K@|hHost@Guf3oVdFTDT62I0VzU+5xC3fGw!DFCY?k|6c?jaC zABt()cJ8c9Ewd)AK9f!!Hki$3m>ej7#S5|fEjoR|$(}$l&98dV8@aikat+jjIO)K`|<-aZYHLNr0x!+06wDRk# zvflc$yliIn{a@ZUe_Vd^{QuYIzkc6c(eeN7veTOT7q5+y+&X3dzq?x^eDAD3Ah=}t z+Lcw_^G!nDm+mQk6}a!-rl#m;?R8#r7riMItk!0qwe9tq!2ApI&To2ledW7pC(3i) z=)4w53UjUvT2igMc6rFrq=V-!KmYOT_<}h+Jsb5)MW()V>$-UR+{S$Q=Ba*ZJnLuN zypu2VH*^AD%C6X4U4dWAf~Ia?{cycU{)-#;UR*H?sZZDEtSNodcLU<}IQOLZzD@t2 zwa!!NYOn5YK2M}dXP4&vzqhXM`!@0T?)Up{$KU7ub8!v-k5B6A_47}DH~QPU`q=wD z`IGhP=f2iw`0CvJ@35|oT*4o{cj5lwnqORB)U9Ma^(JWY#cy}zwx|6(_3G8Ot8UND z*WWOxU!K%)BI=m`%easq)scVYUYVZ#>ALClibV!`=Hl*F3kqGmN;lCi#urvvc$HQ?A~${x&z+px{@@Kc>)ljf43@ zyT2V=*C#parbg(*h^=>5xs{w+m8+q;@AH)#SKEsrDe>mS8-D`JO74dqolX-u9n(!&Pm<7jCJ|luO_QPioIqRA8gqz-paM|-fG>Q zFAM$Vew;qL@*c0B&ze%6Fd5~_gWuoIvOZpyvwnrl?V_mrD`&ip+?%?(p2_HZ@y7LQ zPRZV0m8-J!w7%2S*~u2}+U`p3k9Np*^2(<+e~gWHKI`)JmgMfQpIL(6-wZ>-V&uzS764{pEk`=;L$%AH!HH(tJXJyLzo;_K^9W}Hudk(g~2|Nqr=qx5gr z4E5AB^u0Fw9ryG4yGSYgWXa9I{?ci-x$iGN>(4G-*V8E~w4%-KaD?vLm#0#zy(ZRX z`Wy{4`e?iVkxuqI-G4fZU-T^N%3k&9*R1g0Ka_3G{n7k%%_iMc{A=P)Q&SN<_?Tr}bL$JvHA<6~^^DV^ADICblia##1W$rtQT|N1_S zD_-^S--1djyX*~n?s?DOcSkz>XZXxl)m*LoulBe+=U$lP7M$RE&h~)SH~-6ixz1nt zzf>o*%THZARe7Rv;h(-E%lbDxxUYTpJ;TM#@5@#OmET=ew9v!+(QaXfwXuQ;vtEAm z`fRnlXhpf(X1A1|%N}_=@tAXAvCkPF@Tk-+J4my^u|C(NWPYAS+;{o!Zcll|SnnPa zlG?f4c5h_;{%v*XvSIhH+8nVEvJ|pBb?kh(uDH|*DR=O4WQK$T9W&RiTer`Q)x-Gn zNojZG?{{U4|8}`wDF>a>z`)?c#dzNqG(E#$pvhA6T@+N$H=JYzw;`Zg0w4oasN>cU zsa-`crR8I4-=|;y_xHc~K}Fs@7602e%2oV)Iz2rtP3`xx&6f`N*L|A&1>C|gn7Zk= z{`}my|Ci2|*S&rAw%%^$$8+mFnXmtQD$hPOJjDNBEab?$d7z~S2X^kS$pZ@Unz!?-?b(fqHH3x8lIPHlIu#TX5&kNBmRt|J(8Zwt8#N7M8!{ zley+T^C$1`(1q=iHM8`f>UO-p_ve%S?k}yqo&1?;;Q9VlwdJ#_!5*GbaKHBV>G+*L zTEBMkXR3iOv@_-H_nFU{e&&GN zQTNfiygoH^?fQ@B_WzAs{$%<8Z|0YdtpD?1x&7aR)|GAff73nx^WT4!FS}%B?f1Lv z{-MU!{_;K>-+L{!o;z<+|&gue8H|N3nNG7v7JJkGHSRStsA|NVWRu?Y%Dh?eccMZGP=LMdfVI z&pc(@gzJ``RjbO2&)PWggWGSD-|gQ1uI}HbtNU6x-ktwsYc<(C zf1mWXtv*e~oBTJ&uzOAnk@^@nbHdUWsb7>HKiyq9b!n*UQSQH6Z%cSgyp#F3H!m}y z5!8cj(ENJ$_4ao#ur=F#q%4KX=*xH|K+I?!9z;tF+?To}ce- z-MTe1c~W(hE89f#pL--&^nzWU-%mJu^mHq5|yWR0~<)y9O54X;mpjx+BY3YYMb}zsOg&M5gx4ZiD zw?9j-?rXjA_WZs5xl8W&@&54sxqo`t{xA7;*M9cjf0@qOb>r>o_pc86xR;%Fxu2K3 z@3n|=-oDoo)rKv1o8LcMcANk1QP9SKWmn6~^Y(qdwsh)0*Q_n}f6L>w?$teapL=fp z|Jm`?ufOg8{aNYb^?Uy_r(HU4$wj zAV2Z-m;JvaZJHC=b?zDiC?T3%VqUc{O@GJQb(1HRraJeoyUGk|dxL`Gc6{CIN8-Pv z;(uNLRhMG_YyYMrN-{Jza>y>j2XkmWN{ChajkAP1ffQ@dAQ|MhHqOY-9P ze?9hmx|-dRnTGH|_G2~Wr=YnpW!;~D^Xng;x^}hquDO_=e}M;h`O<#&w6ru>_vq^q z@YFHXO!56i?|*CG|8@R!__l59?%$esVZt&`=J0)gAIJavwf}QyGDHG#{(=kL9(8%elF{r5}E+bb(~cI diff --git a/doc/qtcreator/images/qtcreator-preferences-build-run-qmake.png b/doc/qtcreator/images/qtcreator-preferences-build-run-qmake.png new file mode 100644 index 0000000000000000000000000000000000000000..b5129ada13eaad2411aeabfd4d556717b85a4f77 GIT binary patch literal 4401 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 literal 0 HcmV?d00001 diff --git a/doc/qtcreator/images/qtcreator-preferences-cmake-general.png b/doc/qtcreator/images/qtcreator-preferences-cmake-general.png new file mode 100644 index 0000000000000000000000000000000000000000..fc096178c71995aa7a58d2f614149cf3dd363104 GIT binary patch literal 4094 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 A`~Uy| literal 0 HcmV?d00001 diff --git a/doc/qtcreator/images/qtcreator-preferences-cmake-tools.png b/doc/qtcreator/images/qtcreator-preferences-cmake-tools.png new file mode 100644 index 0000000000000000000000000000000000000000..c7abce197794123d8bb6d462338974c2ba17ac05 GIT binary patch literal 17395 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 u%N5X39iVX*&@?mTx^d6bY@)7ZM?Pa( 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 a)p!42v+aev!6XI-1_n=8KbLh*2~7Z**6F(d literal 6651 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 diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc b/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc index 17f920bc0ab..8c12817c211 100644 --- a/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc +++ b/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc @@ -126,10 +126,10 @@ does not delete the initial configuration. To be asked before \QC resets the changes, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Build & Run} > \uicontrol CMake > + \uicontrol Preferences > \uicontrol CMake > \uicontrol General > \uicontrol {Ask before re-configuring with initial parameters}. - \image qtcreator-build-run-options-cmake.png "CMake Build & Run options" + \image qtcreator-preferences-cmake-general.png "General tab in CMake Preferences" \section1 Viewing CMake Output @@ -205,8 +205,8 @@ \QC can automatically set up the \l {Setting Up Conan} {Conan package manager} for use with CMake. - Select \uicontrol Edit > \uicontrol Preferences > \uicontrol {Build & Run} > - \uicontrol CMake > \uicontrol {Package manager auto setup} to set the + Select \uicontrol Edit > \uicontrol Preferences > \uicontrol CMake + \uicontrol General > \uicontrol {Package manager auto setup} to set the value of the \c CMAKE_PROJECT_INCLUDE_BEFORE variable to the path to a CMake script that installs dependencies from a \c conanfile.txt, \c conanfile.py, or \c vcpkg.json file in the project source directory. diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc b/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc index 76f16cc5ce2..441cfe3a179 100644 --- a/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc +++ b/doc/qtcreator/src/cmake/creator-projects-cmake.qdoc @@ -74,10 +74,10 @@ \list 1 - \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol Kits > - \uicontrol CMake. + \li Select \uicontrol Edit > \uicontrol Preferences > \uicontrol CMake > + \uicontrol Tools. - \image qtcreator-cmakeexecutable.png + \image qtcreator-preferences-cmake-tools.png "Tools tab in CMake Preferences" \li The \uicontrol Name field displays a name for the CMake installation. @@ -107,7 +107,8 @@ To remove the selected CMake executable from the list, select \uicontrol Remove. - Select the \uicontrol Kits tab to add the CMake tool to a build and run kit. + To add the CMake tool to a build and run kit, select \uicontrol Edit > + \uicontrol Preferences > \uicontrol Kits. The kit also specifies the CMake generator that is used for producing project files for \QC and the initial configuration parameters: diff --git a/doc/qtcreator/src/projects/creator-only/creator-build-settings-qmake.qdoc b/doc/qtcreator/src/projects/creator-only/creator-build-settings-qmake.qdoc index d37d8615483..f70ba9a0ea4 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-build-settings-qmake.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-build-settings-qmake.qdoc @@ -50,13 +50,13 @@ > \uicontrol Preferences > \uicontrol {Build & Run} > \uicontrol Qmake. - \image qtcreator-build-settings-qmake.png "qmake build and run options" + \image qtcreator-preferences-build-run-qmake.png "Qmake tab in Build & Run Preferences" To set the default build properties, select \uicontrol Edit > \uicontrol Preferences > \uicontrol {Build & Run} > \uicontrol {Default Build Properties}. - \image qtcreator-build-settings-default.png "default build options" + \image qtcreator-build-settings-default.png "Default Build Properties tab in Build & Run Preferences" \section1 Compiling QML 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 171e259c772..6604121d606 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 @@ -41,11 +41,7 @@ To change the location of the project directory, and to specify settings for building and running projects, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol {Build & Run} > \uicontrol General. The - \uicontrol CMake tab contains additional settings for CMake. You can find - more settings for CMake in \uicontrol Edit > \uicontrol Preferences > - \uicontrol Kits > \uicontrol CMake and for Qbs in \uicontrol Edit > - \uicontrol Preferences > \uicontrol Qbs. + \uicontrol Preferences > \uicontrol {Build & Run} > \uicontrol General. To specify build and run settings for different target platforms, select \uicontrol Projects. For more information on the options you have, diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc index bfdca7d8d56..77805ea2aa1 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-building.qdoc @@ -99,9 +99,11 @@ \QC automatically runs CMake when you make changes to \c {CMakeLists.txt} files. To disable this feature, select \uicontrol Edit > - \uicontrol Preferences > \uicontrol Kits > \uicontrol CMake. Select the CMake - executable to edit, and then deselect the \uicontrol {Autorun CMake} check - box. + \uicontrol Preferences > \uicontrol CMake > \uicontrol Tools. Select the + CMake executable to edit, and then deselect the \uicontrol {Autorun CMake} + check box. + + \image qtcreator-preferences-cmake-tools.png "Tools tab in CMake Preferences" For more information, see \l {Setting Up CMake}. @@ -110,6 +112,10 @@ To prevent failures on incremental builds, it might make sense to always run qmake before building, even though it means that building will take more time. To enable this option, select \uicontrol Edit - > \uicontrol Preferences > \uicontrol {Build & Run} > \uicontrol qmake > + > \uicontrol Preferences > \uicontrol {Build & Run} > \uicontrol Qmake > \uicontrol {Run qmake on every build}. + + \image qtcreator-preferences-build-run-qmake.png "qmake tab in Build & Run Preferences" + + For more information, see \l {qmake Build Configuration}. */ From ddd697c3713e2a2c9cecaf7a45846aad874ae185 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 17 Oct 2022 08:33:04 +0200 Subject: [PATCH 086/143] Don't clear the clipboard at shutdown No idea how or why that was done, but it is destructive. Fixes: QTCREATORBUG-28317 Change-Id: I4fe34ce6b40b81c52bbac31ce384b4e2eb3ad983 Reviewed-by: David Schulz --- src/plugins/modeleditor/modeleditor_plugin.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/modeleditor/modeleditor_plugin.cpp b/src/plugins/modeleditor/modeleditor_plugin.cpp index 86ed69757e1..bcaf8794f0d 100644 --- a/src/plugins/modeleditor/modeleditor_plugin.cpp +++ b/src/plugins/modeleditor/modeleditor_plugin.cpp @@ -83,7 +83,6 @@ void ModelEditorPlugin::extensionsInitialized() ExtensionSystem::IPlugin::ShutdownFlag ModelEditorPlugin::aboutToShutdown() { d->settingsController.save(Core::ICore::settings()); - QApplication::clipboard()->clear(); return SynchronousShutdown; } From 86d41d58a84a37922be2a0ad756d4b003589f94f Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 17 Oct 2022 08:56:10 +0200 Subject: [PATCH 087/143] Squish: Restore some more object names ..and adapt test slightly. Fixes generic highlighter test. Change-Id: I72de310d55ceadc684d15d4e257cd1d90fcf836b Reviewed-by: Eike Ziller --- src/plugins/coreplugin/mimetypesettings.cpp | 3 +++ src/plugins/texteditor/highlightersettingspage.cpp | 1 + tests/system/suite_editors/tst_generic_highlighter/test.py | 6 +++--- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/coreplugin/mimetypesettings.cpp b/src/plugins/coreplugin/mimetypesettings.cpp index 6551cd1a0e8..8f6b4a578fe 100644 --- a/src/plugins/coreplugin/mimetypesettings.cpp +++ b/src/plugins/coreplugin/mimetypesettings.cpp @@ -297,9 +297,11 @@ MimeTypeSettingsPrivate::~MimeTypeSettingsPrivate() = default; void MimeTypeSettingsPrivate::configureUi(QWidget *w) { auto filterLineEdit = new FancyLineEdit; + filterLineEdit->setObjectName("filterLineEdit"); filterLineEdit->setFiltering(true); m_mimeTypesTreeView = new QTreeView; + m_mimeTypesTreeView->setObjectName("mimeTypesTreeView"); m_mimeTypesTreeView->setEditTriggers(QAbstractItemView::DoubleClicked |QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked); m_mimeTypesTreeView->setRootIsDecorated(false); @@ -317,6 +319,7 @@ void MimeTypeSettingsPrivate::configureUi(QWidget *w) patternsLabel->setText(QCoreApplication::translate("Core::Internal::MimeTypeSettingsPage", "Patterns:", nullptr)); m_patternsLineEdit = new QLineEdit; + m_patternsLineEdit->setObjectName("patternsLineEdit"); m_patternsLineEdit->setToolTip(QCoreApplication::translate("Core::Internal::MimeTypeSettingsPage", "A semicolon-separated list of wildcarded file names.", nullptr)); m_magicHeadersTreeWidget = new QTreeWidget; diff --git a/src/plugins/texteditor/highlightersettingspage.cpp b/src/plugins/texteditor/highlightersettingspage.cpp index 3e1067651a2..777bff397ba 100644 --- a/src/plugins/texteditor/highlightersettingspage.cpp +++ b/src/plugins/texteditor/highlightersettingspage.cpp @@ -56,6 +56,7 @@ public: downloadDefinitions->setToolTip(tr("Download missing and update existing syntax definition files.")); updateStatus = new QLabel; + updateStatus->setObjectName("updateStatus"); definitionFilesPath = new PathChooser; definitionFilesPath->setExpectedKind(PathChooser::ExistingDirectory); diff --git a/tests/system/suite_editors/tst_generic_highlighter/test.py b/tests/system/suite_editors/tst_generic_highlighter/test.py index 7e7296bd1ef..f1f154376ec 100644 --- a/tests/system/suite_editors/tst_generic_highlighter/test.py +++ b/tests/system/suite_editors/tst_generic_highlighter/test.py @@ -35,8 +35,8 @@ def getOrModifyFilePatternsFor(mimeType, filter='', toBePresent=None): waitForObject("{container=':Options.qt_tabwidget_tabbar_QTabBar' type='TabItem' " "text='MIME Types'}") clickOnTab(":Options.qt_tabwidget_tabbar_QTabBar", "MIME Types") - replaceEditorContent(waitForObject("{name='filterLineEdit' type='QLineEdit' visible='1'}"), - filter) + replaceEditorContent(waitForObject("{name='filterLineEdit' type='Utils::FancyLineEdit' " + "visible='1'}"), filter) mimeTypeTable = waitForObject("{name='mimeTypesTreeView' type='QTreeView' visible='1'}") model = mimeTypeTable.model() if filter == '': @@ -99,7 +99,7 @@ def addHighlighterDefinition(*languages): "text='Generic Highlighter'}") clickOnTab(":Options.qt_tabwidget_tabbar_QTabBar", "Generic Highlighter") - clickButton("{text='Download Definitions' type='QPushButton' name='downloadDefinitions' visible='1'}") + clickButton("{text='Download Definitions' type='QPushButton' unnamed='1' visible='1'}") updateStatus = "{name='updateStatus' type='QLabel' visible='1'}" waitFor("object.exists(updateStatus)", 5000) if waitFor('str(findObject(updateStatus).text) == "Download finished"', 5000): From e36ff27403e358c5c783a57486f75747561bdcd0 Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Fri, 14 Oct 2022 13:30:50 +0200 Subject: [PATCH 088/143] Tracing: Fix several occurrences of "Unqualified access" in QML code Courtesy of qmllint via qmlls. Change-Id: I28c0a8f7ec17a93831fd1f5d9d2de5547a633965 Reviewed-by: Reviewed-by: Ulf Hermann --- src/libs/tracing/qml/CategoryLabel.qml | 40 +++++++------- src/libs/tracing/qml/FlameGraphDelegate.qml | 12 ++--- src/libs/tracing/qml/MainView.qml | 17 +++--- src/libs/tracing/qml/Overview.qml | 30 +++++------ src/libs/tracing/qml/RangeDetails.qml | 52 ++++++++++--------- src/libs/tracing/qml/RangeMover.qml | 14 ++--- src/libs/tracing/qml/RowLabel.qml | 2 +- src/libs/tracing/qml/SelectionRange.qml | 4 +- .../tracing/qml/SelectionRangeDetails.qml | 15 +++--- src/libs/tracing/qml/TimeDisplay.qml | 3 +- src/libs/tracing/qml/TimeMarks.qml | 7 +-- src/libs/tracing/qml/TimelineContent.qml | 20 +++---- src/libs/tracing/qml/TimelineLabels.qml | 8 +-- src/libs/tracing/qml/TimelineRulers.qml | 14 ++--- src/libs/tracing/qml/TimelineText.qml | 1 - 15 files changed, 126 insertions(+), 113 deletions(-) diff --git a/src/libs/tracing/qml/CategoryLabel.qml b/src/libs/tracing/qml/CategoryLabel.qml index 88dc5e89f46..35c4af3ee79 100644 --- a/src/libs/tracing/qml/CategoryLabel.qml +++ b/src/libs/tracing/qml/CategoryLabel.qml @@ -38,13 +38,14 @@ Item { id: dragArea anchors.fill: txt drag.target: dragger - cursorShape: dragging ? Qt.ClosedHandCursor : Qt.OpenHandCursor - drag.minimumY: dragging ? 0 : -dragOffset // Account for parent change below - drag.maximumY: visibleHeight - (dragging ? 0 : dragOffset) + cursorShape: labelContainer.dragging ? Qt.ClosedHandCursor : Qt.OpenHandCursor + // Account for parent change below + drag.minimumY: labelContainer.dragging ? 0 : -labelContainer.dragOffset + drag.maximumY: labelContainer.visibleHeight - (labelContainer.dragging ? 0 : labelContainer.dragOffset) drag.axis: Drag.YAxis hoverEnabled: true ToolTip { - text: model.tooltip || labelContainer.text + text: labelContainer.model.tooltip || labelContainer.text visible: enabled && parent.containsMouse delay: 1000 } @@ -76,30 +77,30 @@ Item { text: labelContainer.text color: Theme.color(Theme.PanelTextColorLight) - height: model ? model.defaultRowHeight : 0 + height: labelContainer.model ? labelContainer.model.defaultRowHeight : 0 verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } Column { id: labelsArea - property QtObject parentModel: model + property QtObject parentModel: labelContainer.model anchors.top: txt.bottom - visible: expanded + visible: labelContainer.expanded Repeater { - model: expanded ? labels.length : 0 + model: labelContainer.expanded ? labelContainer.labels.length : 0 Loader { id: loader // Initially y == 0 for all the items. Don't enable them until they have been moved // into place. property int offset: (index === 0 || y > 0) ? (y + txt.height) : contentHeight - active: contentBottom > offset + active: labelContainer.contentBottom > offset width: labelContainer.width height: labelsArea.parentModel ? labelsArea.parentModel.rowHeight(index + 1) : 0 sourceComponent: RowLabel { - label: labels[index]; + label: labelContainer.labels[index]; onSelectBySelectionId: { if (labelContainer.model.hasMixedTypesInExpandedState) return; @@ -127,11 +128,12 @@ Item { property var texts: [] property int currentNote: -1 Connections { - target: notesModel + target: labelContainer.notesModel function onChanged(typeId, modelId, timelineIndex) { // This will only be called if notesModel != null. - if (modelId === -1 || modelId === model.modelId) { - var notes = notesModel.byTimelineModel(model.modelId); + if (modelId === -1 || modelId === labelContainer.model.modelId) { + var notes = + labelContainer.notesModel.byTimelineModel(labelContainer.model.modelId); var newTexts = []; var newEventIds = []; for (var i in notes) { @@ -161,11 +163,11 @@ Item { anchors.verticalCenter: txt.verticalCenter anchors.right: parent.right implicitHeight: txt.height - 1 - enabled: expanded || (model && !model.empty) - imageSource: expanded ? "image://icons/close_split" : "image://icons/split" - ToolTip.text: expanded ? qsTranslate("Tracing", "Collapse category") - : qsTranslate("Tracing", "Expand category") - onClicked: model.expanded = !expanded + enabled: labelContainer.expanded || (labelContainer.model && !labelContainer.model.empty) + imageSource: labelContainer.expanded ? "image://icons/close_split" : "image://icons/split" + ToolTip.text: labelContainer.expanded ? qsTranslate("Tracing", "Collapse category") + : qsTranslate("Tracing", "Expand category") + onClicked: labelContainer.model.expanded = !labelContainer.expanded } Rectangle { @@ -199,7 +201,7 @@ Item { when: dragger.Drag.active ParentChange { target: dragger - parent: draggerParent + parent: labelContainer.draggerParent } PropertyChanges { target: dragger diff --git a/src/libs/tracing/qml/FlameGraphDelegate.qml b/src/libs/tracing/qml/FlameGraphDelegate.qml index ac36659a1a3..b80533776e0 100644 --- a/src/libs/tracing/qml/FlameGraphDelegate.qml +++ b/src/libs/tracing/qml/FlameGraphDelegate.qml @@ -26,18 +26,18 @@ Item { x: parent === null ? 0 : parent.width * FlameGraph.relativePosition Rectangle { - border.color: borderColor - border.width: borderWidth - color: Qt.hsla((level % 12) / 72, 0.9 + Math.random() / 10, + border.color: flamegraphItem.borderColor + border.width: flamegraphItem.borderWidth + color: Qt.hsla((flamegraphItem.level % 12) / 72, 0.9 + Math.random() / 10, 0.45 + Math.random() / 10, 0.9 + Math.random() / 10); - height: itemHeight; + height: flamegraphItem.itemHeight anchors.left: flamegraphItem.left anchors.right: flamegraphItem.right anchors.bottom: flamegraphItem.bottom TimelineText { id: text - visible: textVisible + visible: flamegraphItem.textVisible anchors.fill: parent anchors.margins: 5 verticalAlignment: Text.AlignVCenter @@ -45,7 +45,7 @@ Item { text: flamegraphItem.text elide: Text.ElideRight wrapMode: Text.WrapAtWordBoundaryOrAnywhere - font.bold: isSelected + font.bold: flamegraphItem.isSelected } MouseArea { diff --git a/src/libs/tracing/qml/MainView.qml b/src/libs/tracing/qml/MainView.qml index 119892a1cec..af6c259f77e 100644 --- a/src/libs/tracing/qml/MainView.qml +++ b/src/libs/tracing/qml/MainView.qml @@ -129,7 +129,7 @@ Rectangle { color: Theme.color(Theme.PanelStatusBarBackgroundColor) modelProxy: timelineModelAggregator zoomer: zoomControl - reverseSelect: shiftPressed + reverseSelect: root.shiftPressed onMoveCategories: (sourceIndex, targetIndex) => { content.moveCategories(sourceIndex, targetIndex) @@ -228,7 +228,7 @@ Rectangle { MouseArea { id: selectionRangeControl - enabled: selectionRangeMode && + enabled: root.selectionRangeMode && selectionRange.creationState !== selectionRange.creationFinished anchors.right: content.right anchors.left: buttonsBar.right @@ -269,7 +269,7 @@ Rectangle { interactive: false x: content.x y: content.y - height: (selectionRangeMode && + height: (root.selectionRangeMode && selectionRange.creationState !== selectionRange.creationInactive) ? content.height : 0 width: content.width @@ -328,7 +328,7 @@ Rectangle { endTime: zoomControl.selectionEnd referenceDuration: zoomControl.rangeDuration showDuration: selectionRange.rangeWidth > 1 - hasContents: selectionRangeMode && + hasContents: root.selectionRangeMode && selectionRange.creationState !== selectionRange.creationInactive onRecenter: { @@ -356,7 +356,7 @@ Rectangle { locked: content.selectionLocked onRecenterOnItem: { - content.select(selectedModel, selectedItem) + content.select(root.selectedModel, root.selectedItem) } onLockedChanged: { @@ -368,10 +368,11 @@ Rectangle { } onUpdateNote: (text) => { - if (timelineModelAggregator.notes && selectedModel != -1 && selectedItem != -1) { + if (timelineModelAggregator.notes && root.selectedModel != -1 + && root.selectedItem != -1) { timelineModelAggregator.notes.setText( - timelineModelAggregator.models[selectedModel].modelId, - selectedItem, text); + timelineModelAggregator.models[root.selectedModel].modelId, + root.selectedItem, text); } } diff --git a/src/libs/tracing/qml/Overview.qml b/src/libs/tracing/qml/Overview.qml index e0a02c39317..0e7a07a603d 100644 --- a/src/libs/tracing/qml/Overview.qml +++ b/src/libs/tracing/qml/Overview.qml @@ -46,8 +46,8 @@ Rectangle { } Connections { - target: zoomer - function onRangeChanged() { updateRangeMover(); } + target: overview.zoomer + function onRangeChanged() { overview.updateRangeMover(); } } TimeDisplay { @@ -59,9 +59,9 @@ Rectangle { height: 10 fontSize: 6 labelsHeight: 10 - windowStart: zoomer.traceStart - alignedWindowStart: zoomer.traceStart - rangeDuration: zoomer.traceDuration + windowStart: overview.zoomer.traceStart + alignedWindowStart: overview.zoomer.traceStart + rangeDuration: overview.zoomer.traceDuration contentX: 0 offsetX: 0 } @@ -75,35 +75,35 @@ Rectangle { id: renderArea Repeater { - model: modelProxy.models + model: overview.modelProxy.models TimelineOverviewRenderer { model: modelData zoomer: overview.zoomer - notes: modelProxy.notes + notes: overview.modelProxy.notes width: renderArea.width - height: renderArea.height / modelProxy.models.length + height: renderArea.height / overview.modelProxy.models.length } } } Repeater { id: noteSigns - property var modelsById: modelProxy.models.reduce(function(prev, model) { + property var modelsById: overview.modelProxy.models.reduce(function(prev, model) { prev[model.modelId] = model; return prev; }, {}); property int vertSpace: renderArea.height / 7 property color noteColor: Theme.color(Theme.Timeline_HighlightColor) - readonly property double spacing: parent.width / zoomer.traceDuration + readonly property double spacing: parent.width / overview.zoomer.traceDuration - model: modelProxy.notes ? modelProxy.notes.count : 0 + model: overview.modelProxy.notes ? overview.modelProxy.notes.count : 0 Item { - property int timelineIndex: modelProxy.notes.timelineIndex(index) - property int timelineModel: modelProxy.notes.timelineModel(index) + property int timelineIndex: overview.modelProxy.notes.timelineIndex(index) + property int timelineModel: overview.modelProxy.notes.timelineModel(index) property double startTime: noteSigns.modelsById[timelineModel].startTime(timelineIndex) property double endTime: noteSigns.modelsById[timelineModel].endTime(timelineIndex) - x: ((startTime + endTime) / 2 - zoomer.traceStart) * noteSigns.spacing + x: ((startTime + endTime) / 2 - overview.zoomer.traceStart) * noteSigns.spacing y: timebar.height + noteSigns.vertSpace height: noteSigns.vertSpace * 5 width: 2 @@ -156,7 +156,7 @@ Rectangle { RangeMover { id: rangeMover - visible: modelProxy.height > 0 + visible: overview.modelProxy.height > 0 onRangeLeftChanged: overview.updateZoomer() onRangeRightChanged: overview.updateZoomer() } diff --git a/src/libs/tracing/qml/RangeDetails.qml b/src/libs/tracing/qml/RangeDetails.qml index 14d6aa9e7af..6998767bea3 100644 --- a/src/libs/tracing/qml/RangeDetails.qml +++ b/src/libs/tracing/qml/RangeDetails.qml @@ -60,9 +60,9 @@ Item { Rectangle { id: titleBar width: parent.width - height: titleBarHeight + height: rangeDetails.titleBarHeight color: Theme.color(Theme.Timeline_PanelHeaderColor) - border.width: borderWidth + border.width: rangeDetails.borderWidth border.color: Theme.color(Theme.PanelTextColorMid) TimelineText { @@ -72,8 +72,8 @@ Item { verticalAlignment: Text.AlignVCenter anchors.left: parent.left anchors.right: closeIcon.left - anchors.leftMargin: outerMargin - anchors.rightMargin: innerMargin + anchors.leftMargin: rangeDetails.outerMargin + anchors.rightMargin: rangeDetails.innerMargin anchors.top: parent.top anchors.bottom: parent.bottom color: Theme.color(Theme.PanelTextColorLight) @@ -93,11 +93,11 @@ Item { ImageToolButton { id: lockIcon - imageSource: "image://icons/lock_" + (locked ? "closed" : "open") + imageSource: "image://icons/lock_" + (rangeDetails.locked ? "closed" : "open") anchors.top: closeIcon.top anchors.right: closeIcon.left implicitHeight: typeTitle.height - onClicked: locked = !locked + onClicked: rangeDetails.locked = !rangeDetails.locked ToolTip.text: qsTranslate("Tracing", "View event information on mouseover.") } @@ -121,7 +121,7 @@ Item { anchors.right: parent.right anchors.bottom: dragHandle.bottom - border.width: borderWidth + border.width: rangeDetails.borderWidth border.color: Theme.color(Theme.PanelTextColorMid) } @@ -130,17 +130,17 @@ Item { anchors.left: parent.left anchors.top: titleBar.bottom - anchors.topMargin: innerMargin - anchors.leftMargin: outerMargin - anchors.rightMargin: outerMargin + anchors.topMargin: rangeDetails.innerMargin + anchors.leftMargin: rangeDetails.outerMargin + anchors.rightMargin: rangeDetails.outerMargin - spacing: innerMargin + spacing: rangeDetails.innerMargin columns: 2 - property int minimumWidth: minimumInnerWidth + property int minimumWidth: rangeDetails.minimumInnerWidth onPositioningComplete: { // max(width of longest label * 2, minimumInnerWidth) - var result = minimumInnerWidth; + var result = rangeDetails.minimumInnerWidth; for (var i = 0; i < children.length; ++i) { if (children[i].isLabel) result = Math.max(children[i].implicitWidth * 2 + innerMargin, result); @@ -149,12 +149,14 @@ Item { minimumWidth = result + 2 * outerMargin; } - property int labelWidth: Math.ceil((minimumWidth - innerMargin) / 2) - outerMargin - property int valueWidth: dragHandle.x - labelWidth - innerMargin - outerMargin + property int labelWidth: Math.ceil((minimumWidth - rangeDetails.innerMargin) / 2) + - rangeDetails.outerMargin + property int valueWidth: dragHandle.x - labelWidth - rangeDetails.innerMargin + - rangeDetails.outerMargin onMinimumWidthChanged: { - if (dragHandle.x < minimumWidth - outerMargin) - dragHandle.x = minimumWidth - outerMargin; + if (dragHandle.x < minimumWidth - rangeDetails.outerMargin) + dragHandle.x = minimumWidth - rangeDetails.outerMargin; } Repeater { @@ -174,9 +176,9 @@ Item { anchors.left: parent.left anchors.right: parent.right - anchors.leftMargin: outerMargin - anchors.rightMargin: outerMargin - anchors.topMargin: visible ? innerMargin : 0 + anchors.leftMargin: rangeDetails.outerMargin + anchors.rightMargin: rangeDetails.outerMargin + anchors.topMargin: visible ? rangeDetails.innerMargin : 0 anchors.top: col.bottom height: visible ? implicitHeight : 0 @@ -201,7 +203,7 @@ Item { Timer { id: saveTimer onTriggered: { - if (!rangeDetails.readOnly) + if (!noteEdit.readOnly) rangeDetails.updateNote(noteEdit.text); } interval: 1000 @@ -211,15 +213,15 @@ Item { Item { id: dragHandle - width: outerMargin - height: outerMargin - x: initialWidth + width: rangeDetails.outerMargin + height: rangeDetails.outerMargin + x: rangeDetails.initialWidth anchors.top: noteEdit.bottom clip: true MouseArea { anchors.fill: parent drag.target: parent - drag.minimumX: col.minimumWidth - outerMargin + drag.minimumX: col.minimumWidth - rangeDetails.outerMargin drag.axis: Drag.XAxis cursorShape: Qt.SizeHorCursor } diff --git a/src/libs/tracing/qml/RangeMover.qml b/src/libs/tracing/qml/RangeMover.qml index c74489b2e79..eac5a1e2d45 100644 --- a/src/libs/tracing/qml/RangeMover.qml +++ b/src/libs/tracing/qml/RangeMover.qml @@ -30,7 +30,9 @@ Item { return Qt.rgba(color.r, color.g, color.b, Math.max(Math.min(color.a, 0.7), 0.3)); } - color: width > 1 ? alphaColor(dragArea.pressed ? dragColor : rangeColor) : singleLineColor + color: width > 1 ? alphaColor(dragArea.pressed ? rangeMover.dragColor + : rangeMover.rangeColor) + : rangeMover.singleLineColor } Item { @@ -49,7 +51,7 @@ Item { height: parent.height anchors.right: parent.left width: 7 - color: handleColor + color: rangeMover.handleColor visible: false Image { source: "image://icons/range_handle" @@ -72,7 +74,7 @@ Item { anchors.fill: leftBorderHandle drag.target: leftRange - drag.axis: "XAxis" + drag.axis: Drag.XAxis drag.minimumX: 0 drag.maximumX: rangeMover.width drag.onActiveChanged: drag.maximumX = rightRange.x @@ -102,7 +104,7 @@ Item { height: parent.height anchors.left: parent.right width: 7 - color: handleColor + color: rangeMover.handleColor visible: false Image { source: "image://icons/range_handle" @@ -125,7 +127,7 @@ Item { anchors.fill: rightBorderHandle drag.target: rightRange - drag.axis: "XAxis" + drag.axis: Drag.XAxis drag.minimumX: 0 drag.maximumX: rangeMover.width drag.onActiveChanged: drag.minimumX = leftRange.x @@ -150,7 +152,7 @@ Item { anchors.fill: selectedRange drag.target: leftRange - drag.axis: "XAxis" + drag.axis: Drag.XAxis drag.minimumX: 0 drag.maximumX: rangeMover.width - origWidth drag.onActiveChanged: origWidth = selectedRange.width diff --git a/src/libs/tracing/qml/RowLabel.qml b/src/libs/tracing/qml/RowLabel.qml index 37cab676619..16a70b53507 100644 --- a/src/libs/tracing/qml/RowLabel.qml +++ b/src/libs/tracing/qml/RowLabel.qml @@ -42,7 +42,7 @@ Button { onPressed: resizing = true onReleased: resizing = false - height: dragHeight + height: button.dragHeight anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right diff --git a/src/libs/tracing/qml/SelectionRange.qml b/src/libs/tracing/qml/SelectionRange.qml index 092b3b84c50..609159b7238 100644 --- a/src/libs/tracing/qml/SelectionRange.qml +++ b/src/libs/tracing/qml/SelectionRange.qml @@ -45,8 +45,8 @@ RangeMover { onRangeLeftChanged: updateZoomer() Connections { - target: zoomer - function onWindowChanged() { updateRange(); } + target: selectionRange.zoomer + function onWindowChanged() { selectionRange.updateRange(); } } function setPos(pos) { diff --git a/src/libs/tracing/qml/SelectionRangeDetails.qml b/src/libs/tracing/qml/SelectionRangeDetails.qml index e837135121b..a40e9753018 100644 --- a/src/libs/tracing/qml/SelectionRangeDetails.qml +++ b/src/libs/tracing/qml/SelectionRangeDetails.qml @@ -25,8 +25,8 @@ Item { // keep inside view Connections { target: selectionRangeDetails.parent - function onWidthChanged() { fitInView(); } - function onHeightChanged() { fitInView(); } + function onWidthChanged() { selectionRangeDetails.fitInView(); } + function onHeightChanged() { selectionRangeDetails.fitInView(); } } function fitInView() { @@ -79,14 +79,17 @@ Item { id: details property var contents: [ qsTranslate("Tracing", "Start") + ":", - TimeFormatter.format(startTime, referenceDuration), + TimeFormatter.format(selectionRangeDetails.startTime, + selectionRangeDetails.referenceDuration), (qsTranslate("Tracing", "End") + ":"), - TimeFormatter.format(endTime, referenceDuration), + TimeFormatter.format(selectionRangeDetails.endTime, + selectionRangeDetails.referenceDuration), (qsTranslate("Tracing", "Duration") + ":"), - TimeFormatter.format(duration, referenceDuration) + TimeFormatter.format(selectionRangeDetails.duration, + selectionRangeDetails.referenceDuration) ] - model: showDuration ? 6 : 2 + model: selectionRangeDetails.showDuration ? 6 : 2 Detail { isLabel: index % 2 === 0 text: details.contents[index] diff --git a/src/libs/tracing/qml/TimeDisplay.qml b/src/libs/tracing/qml/TimeDisplay.qml index 3748a6f7391..904c72ea711 100644 --- a/src/libs/tracing/qml/TimeDisplay.qml +++ b/src/libs/tracing/qml/TimeDisplay.qml @@ -40,7 +40,8 @@ Item { id: timeDisplayArea property int firstBlock: timeDisplay.offsetX / timeDisplay.pixelsPerBlock - property int offset: repeater.model > 0 ? repeater.model - (firstBlock % repeater.model) : 0; + property int offset: repeater.model > 0 ? repeater.model - (firstBlock % repeater.model) + : 0; Repeater { id: repeater diff --git a/src/libs/tracing/qml/TimeMarks.qml b/src/libs/tracing/qml/TimeMarks.qml index 8608efdbd0e..7583495d781 100644 --- a/src/libs/tracing/qml/TimeMarks.qml +++ b/src/libs/tracing/qml/TimeMarks.qml @@ -78,11 +78,11 @@ Item { property double maxVal: scope.model ? scope.model.rowMaxValue(index) : 0 property double valDiff: maxVal - minVal property bool scaleVisible: scope.model && scope.model.expanded && - height > scaleMinHeight && valDiff > 0 + height > timeMarks.scaleMinHeight && valDiff > 0 property double stepVal: { var ret = 1; - var ugly = Math.ceil(valDiff / Math.floor(height / scaleStepping)); + var ugly = Math.ceil(valDiff / Math.floor(height / timeMarks.scaleStepping)); while (isFinite(ugly) && ugly > 1) { ugly /= 2; ret *= 2; @@ -122,7 +122,8 @@ Item { anchors.bottomMargin: 2 anchors.leftMargin: 2 anchors.left: parent.left - text: prettyPrintScale(scaleItem.minVal + index * scaleItem.stepVal) + text: prettyPrintScale(scaleItem.minVal + + index * scaleItem.stepVal) } Rectangle { diff --git a/src/libs/tracing/qml/TimelineContent.qml b/src/libs/tracing/qml/TimelineContent.qml index ec26943dab8..7788f6d772d 100644 --- a/src/libs/tracing/qml/TimelineContent.qml +++ b/src/libs/tracing/qml/TimelineContent.qml @@ -96,11 +96,11 @@ Flickable { DelegateModel { id: timelineModel - model: modelProxy.models + model: flick.modelProxy.models delegate: TimelineRenderer { id: renderer model: modelData - notes: modelProxy.notes + notes: flick.modelProxy.notes zoomer: flick.zoomer selectionLocked: flick.selectionLocked x: 0 @@ -124,29 +124,29 @@ Flickable { } function recenter() { - if (modelData.endTime(selectedItem) < zoomer.rangeStart || - modelData.startTime(selectedItem) > zoomer.rangeEnd) { + if (modelData.endTime(renderer.selectedItem) < zoomer.rangeStart || + modelData.startTime(renderer.selectedItem) > zoomer.rangeEnd) { - var newStart = Math.max((modelData.startTime(selectedItem) + - modelData.endTime(selectedItem) - + var newStart = Math.max((modelData.startTime(renderer.selectedItem) + + modelData.endTime(renderer.selectedItem) - zoomer.rangeDuration) / 2, zoomer.traceStart); zoomer.setRange(newStart, Math.min(newStart + zoomer.rangeDuration, zoomer.traceEnd)); } - var row = modelData.row(selectedItem); + var row = renderer.model.row(renderer.selectedItem); var rowStart = modelData.rowOffset(row) + y; var rowEnd = rowStart + modelData.rowHeight(row); if (rowStart < flick.contentY || rowEnd - flick.height > flick.contentY) flick.contentY = (rowStart + rowEnd - flick.height) / 2; } - onSelectedItemChanged: flick.propagateSelection(index, selectedItem); + onSelectedItemChanged: flick.propagateSelection(index, renderer.selectedItem); Connections { - target: model + target: renderer.model function onDetailsChanged() { - if (selectedItem != -1) { + if (renderer.selectedItem != -1) { flick.propagateSelection(-1, -1); flick.propagateSelection(index, selectedItem); } diff --git a/src/libs/tracing/qml/TimelineLabels.qml b/src/libs/tracing/qml/TimelineLabels.qml index ea684957ab4..990d04aa96b 100644 --- a/src/libs/tracing/qml/TimelineLabels.qml +++ b/src/libs/tracing/qml/TimelineLabels.qml @@ -38,7 +38,7 @@ Flickable { // As we cannot retrieve items by visible index we keep an array of row counts here, // for the time marks to draw the row backgrounds in the right colors. - property var rowCounts: new Array(modelProxy.models.length) + property var rowCounts: new Array(categories.modelProxy.models.length) function updateRowCount(visualIndex, rowCount) { if (rowCounts[visualIndex] !== rowCount) { @@ -48,7 +48,7 @@ Flickable { } } - model: modelProxy.models + model: categories.modelProxy.models delegate: Loader { id: loader asynchronous: y < categories.contentY + categories.height && @@ -73,7 +73,7 @@ Flickable { CategoryLabel { id: label model: modelData - notesModel: modelProxy.notes + notesModel: categories.modelProxy.notes visualIndex: loader.visualIndex dragging: categories.dragging reverseSelect: categories.reverseSelect @@ -114,7 +114,7 @@ Flickable { TimeMarks { id: timeMarks model: modelData - mockup: modelProxy.height === 0 + mockup: categories.modelProxy.height === 0 anchors.right: parent.right anchors.left: label.right anchors.top: parent.top diff --git a/src/libs/tracing/qml/TimelineRulers.qml b/src/libs/tracing/qml/TimelineRulers.qml index 0cc676d0562..65e04c5281e 100644 --- a/src/libs/tracing/qml/TimelineRulers.qml +++ b/src/libs/tracing/qml/TimelineRulers.qml @@ -20,7 +20,7 @@ Item { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right - height: scaleHeight + height: rulersParent.scaleHeight onClicked: (mouse) => { rulersModel.append({ @@ -36,7 +36,8 @@ Item { if (index >= 0) { rulersModel.setProperty( index, "timestamp", - (x + contentX) * viewTimePerPixel + windowStart); + (x + rulersParent.contentX) * rulersParent.viewTimePerPixel + + rulersParent.windowStart); } } } @@ -46,14 +47,15 @@ Item { Item { id: ruler - x: (timestamp - windowStart) / viewTimePerPixel - 1 - contentX + x: (timestamp - rulersParent.windowStart) / rulersParent.viewTimePerPixel + - 1 - rulersParent.contentX y: 0 width: 2 height: rulersParent.height Rectangle { id: arrow - height: scaleHeight - width: scaleHeight + height: rulersParent.scaleHeight + width: rulersParent.scaleHeight rotation: 45 anchors.verticalCenter: parent.top anchors.horizontalCenter: parent.horizontalCenter @@ -86,7 +88,7 @@ Item { Rectangle { anchors.top: arrow.bottom anchors.horizontalCenter: ruler.horizontalCenter - width: scaleHeight / 4 + width: rulersParent.scaleHeight / 4 height: width color: Theme.color(Theme.Timeline_PanelBackgroundColor) diff --git a/src/libs/tracing/qml/TimelineText.qml b/src/libs/tracing/qml/TimelineText.qml index 32b53fa10dd..55e11e5a963 100644 --- a/src/libs/tracing/qml/TimelineText.qml +++ b/src/libs/tracing/qml/TimelineText.qml @@ -11,4 +11,3 @@ Text { renderType: Text.NativeRendering color: Theme.color(Theme.Timeline_TextColor) } - From ca4566f3e819d05b495af3a1009c1c86d3963aab Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Fri, 14 Oct 2022 19:24:19 +0200 Subject: [PATCH 089/143] QmlProfiler: Remove access to non-existent TimeInPercentRole Change-Id: I4b909164d7d69e1fb1902ffde2a89ffdda455751 Reviewed-by: Reviewed-by: Ulf Hermann --- src/plugins/qmlprofiler/qml/QmlProfilerFlameGraphView.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/qmlprofiler/qml/QmlProfilerFlameGraphView.qml b/src/plugins/qmlprofiler/qml/QmlProfilerFlameGraphView.qml index 9926ce460b1..40c95a14cf0 100644 --- a/src/plugins/qmlprofiler/qml/QmlProfilerFlameGraphView.qml +++ b/src/plugins/qmlprofiler/qml/QmlProfilerFlameGraphView.qml @@ -28,7 +28,6 @@ FlameGraphView { QmlProfilerFlameGraphModel.CallCountRole, qsTranslate("QmlProfiler", "Calls"), QmlProfilerFlameGraphModel.DetailsRole, qsTranslate("QmlProfiler", "Details"), QmlProfilerFlameGraphModel.TimePerCallRole, qsTranslate("QmlProfiler", "Mean Time"), - QmlProfilerFlameGraphModel.TimeInPercentRole, qsTranslate("QmlProfiler", "In Percent"), QmlProfilerFlameGraphModel.LocationRole, qsTranslate("QmlProfiler", "Location"), QmlProfilerFlameGraphModel.AllocationsRole, qsTranslate("QmlProfiler", "Allocations"), QmlProfilerFlameGraphModel.MemoryRole, qsTranslate("QmlProfiler", "Memory") From b983b8aa52d9f919f3ed3ee793a30835706a45a8 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Wed, 12 Oct 2022 20:46:17 +0300 Subject: [PATCH 090/143] QmlDesigner: Implement unimporting bundle materials Fixes: QDS-7904 Change-Id: I08642c25a2844547d0104a7b3d9fda6afe47cd38 Reviewed-by: Reviewed-by: Miikka Heikkinen --- .../BundleMaterialItem.qml | 46 +++++++++- .../MaterialBrowser.qml | 38 ++++++--- .../UnimportBundleMaterialDialog.qml | 85 +++++++++++++++++++ .../materialbrowser/bundleimporter.h | 2 +- .../materialbrowser/bundlematerial.cpp | 16 ++++ .../materialbrowser/bundlematerial.h | 6 ++ .../bundlematerialcategory.cpp | 10 +++ .../materialbrowser/bundlematerialcategory.h | 1 + .../materialbrowserbundlemodel.cpp | 50 ++++++++++- .../materialbrowserbundlemodel.h | 13 ++- .../materialbrowser/materialbrowserview.cpp | 39 ++++++++- .../materialbrowser/materialbrowserview.h | 2 + 12 files changed, 287 insertions(+), 21 deletions(-) create mode 100644 share/qtcreator/qmldesigner/materialBrowserQmlSource/UnimportBundleMaterialDialog.qml diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/BundleMaterialItem.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/BundleMaterialItem.qml index 4b8b45be564..59541f47d39 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/BundleMaterialItem.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/BundleMaterialItem.qml @@ -27,6 +27,8 @@ import QtQuick 2.15 import QtQuick.Layouts 1.15 import QtQuickDesignerTheme 1.0 import HelperWidgets 2.0 +import QtQuick.Controls + import StudioTheme 1.0 as StudioTheme Item { @@ -43,7 +45,7 @@ Item { acceptedButtons: Qt.LeftButton | Qt.RightButton onPressed: (mouse) => { - if (mouse.button === Qt.LeftButton) + if (mouse.button === Qt.LeftButton && !materialBrowserBundleModel.importerRunning) rootView.startDragBundleMaterial(modelData, mapToGlobal(mouse.x, mouse.y)) else if (mouse.button === Qt.RightButton) root.showContextMenu() @@ -64,6 +66,48 @@ Item { anchors.horizontalCenter: parent.horizontalCenter source: modelData.bundleMaterialIcon cache: false + + Rectangle { // circular indicator for imported bundle materials + width: 10 + height: 10 + radius: 5 + anchors.right: img.right + anchors.top: img.top + anchors.margins: 5 + color: "#00ff00" + border.color: "#555555" + border.width: 1 + visible: modelData.bundleMaterialImported + + ToolTip { + visible: indicatorMouseArea.containsMouse + text: qsTr("Material is imported to project") + delay: 1000 + } + + MouseArea { + id: indicatorMouseArea + anchors.fill: parent + hoverEnabled: true + } + } + + IconButton { + icon: StudioTheme.Constants.plus + tooltip: qsTr("Add an instance to project") + buttonSize: 22 + property color c: StudioTheme.Values.themeIconColor + normalColor: Qt.hsla(c.hslHue, c.hslSaturation, c.hslLightness, .2) + hoverColor: Qt.hsla(c.hslHue, c.hslSaturation, c.hslLightness, .3) + pressColor: Qt.hsla(c.hslHue, c.hslSaturation, c.hslLightness, .4) + anchors.right: img.right + anchors.bottom: img.bottom + enabled: !materialBrowserBundleModel.importerRunning + + onClicked: { + materialBrowserBundleModel.addToProject(modelData) + } + } } TextInput { diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml index 2fecc7582ad..19458a93f55 100644 --- a/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/MaterialBrowser.qml @@ -46,8 +46,8 @@ Item { // Called also from C++ to close context menu on focus out function closeContextMenu() { - cxtMenu.close() - cxtMenuBundle.close() + ctxMenu.close() + ctxMenuBundle.close() } // Called from C++ to refresh a preview material after it changes @@ -79,7 +79,7 @@ Item { if (!materialBrowserModel.hasMaterialRoot && (!materialBrowserBundleModel.matBundleExists || mouse.y < userMatsSecBottom)) { root.currentMaterial = null - cxtMenu.popup() + ctxMenu.popup() } } } @@ -97,8 +97,12 @@ Item { } } + UnimportBundleMaterialDialog { + id: unimportBundleMaterialDialog + } + StudioControls.Menu { - id: cxtMenu + id: ctxMenu closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside @@ -205,7 +209,7 @@ Item { } StudioControls.Menu { - id: cxtMenuBundle + id: ctxMenuBundle closePolicy: StudioControls.Menu.CloseOnEscape | StudioControls.Menu.CloseOnPressOutside @@ -224,9 +228,22 @@ Item { StudioControls.MenuSeparator {} StudioControls.MenuItem { - text: qsTr("Add to project") + enabled: !materialBrowserBundleModel.importerRunning + text: qsTr("Add an instance to project") - onTriggered: materialBrowserBundleModel.addMaterial(root.currentBundleMaterial) + onTriggered: { + materialBrowserBundleModel.addToProject(root.currentBundleMaterial) + } + } + + StudioControls.MenuItem { + enabled: !materialBrowserBundleModel.importerRunning && root.currentBundleMaterial.bundleMaterialImported + text: qsTr("Remove from project") + + onTriggered: { + unimportBundleMaterialDialog.targetBundleMaterial = root.currentBundleMaterial + unimportBundleMaterialDialog.open() + } } } @@ -301,13 +318,14 @@ Item { height: root.height - searchBox.height clip: true visible: materialBrowserModel.hasQuick3DImport && !materialBrowserModel.hasMaterialRoot + interactive: !ctxMenu.opened && !ctxMenuBundle.opened Column { Section { id: userMaterialsSection width: root.width - caption: qsTr("User materials") + caption: qsTr("Materials") hideHeader: !materialBrowserBundleModel.matBundleExists Grid { @@ -329,7 +347,7 @@ Item { onShowContextMenu: { root.currentMaterial = model - cxtMenu.popup() + ctxMenu.popup() } } } @@ -392,7 +410,7 @@ Item { onShowContextMenu: { root.currentBundleMaterial = modelData - cxtMenuBundle.popup() + ctxMenuBundle.popup() } } } diff --git a/share/qtcreator/qmldesigner/materialBrowserQmlSource/UnimportBundleMaterialDialog.qml b/share/qtcreator/qmldesigner/materialBrowserQmlSource/UnimportBundleMaterialDialog.qml new file mode 100644 index 00000000000..82708467a8a --- /dev/null +++ b/share/qtcreator/qmldesigner/materialBrowserQmlSource/UnimportBundleMaterialDialog.qml @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuickDesignerTheme +import HelperWidgets +import StudioControls as StudioControls +import StudioTheme as StudioTheme + +Dialog { + id: root + + title: qsTr("Bundle material might be in use") + anchors.centerIn: parent + closePolicy: Popup.CloseOnEscape + implicitWidth: 300 + modal: true + + property var targetBundleMaterial + + contentItem: Column { + spacing: 20 + width: parent.width + + Text { + id: folderNotEmpty + + text: qsTr("If the material you are removing is in use, it might cause the project to malfunction.\n\nAre you sure you want to remove the material?") + color: StudioTheme.Values.themeTextColor + wrapMode: Text.WordWrap + anchors.right: parent.right + anchors.left: parent.left + leftPadding: 10 + rightPadding: 10 + + Keys.onEnterPressed: btnRemove.onClicked() + Keys.onReturnPressed: btnRemove.onClicked() + } + + Row { + anchors.right: parent.right + Button { + id: btnRemove + + text: qsTr("Remove") + + onClicked: { + materialBrowserBundleModel.removeFromProject(root.targetBundleMaterial) + root.accept() + } + } + + Button { + text: qsTr("Cancel") + onClicked: root.reject() + } + } + } + + onOpened: folderNotEmpty.forceActiveFocus() +} diff --git a/src/plugins/qmldesigner/components/materialbrowser/bundleimporter.h b/src/plugins/qmldesigner/components/materialbrowser/bundleimporter.h index 599d4d8e50b..64344915ce1 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/bundleimporter.h +++ b/src/plugins/qmldesigner/components/materialbrowser/bundleimporter.h @@ -51,6 +51,7 @@ public: QString importComponent(const QString &qmlFile, const QStringList &files); QString unimportComponent(const QString &qmlFile); + Utils::FilePath resolveBundleImportPath(); signals: // The metaInfo parameter will be invalid if an error was encountered during @@ -63,7 +64,6 @@ private: void handleImportTimer(); QVariantHash loadAssetRefMap(const Utils::FilePath &bundlePath); void writeAssetRefMap(const Utils::FilePath &bundlePath, const QVariantHash &assetRefMap); - Utils::FilePath resolveBundleImportPath(); Utils::FilePath m_bundleDir; QString m_bundleId; diff --git a/src/plugins/qmldesigner/components/materialbrowser/bundlematerial.cpp b/src/plugins/qmldesigner/components/materialbrowser/bundlematerial.cpp index b5bc19e785c..6cee76475ac 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/bundlematerial.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/bundlematerial.cpp @@ -70,4 +70,20 @@ bool BundleMaterial::visible() const return m_visible; } +bool BundleMaterial::setImported(bool imported) +{ + if (m_imported != imported) { + m_imported = imported; + emit materialImportedChanged(); + return true; + } + + return false; +} + +bool BundleMaterial::imported() const +{ + return m_imported; +} + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/materialbrowser/bundlematerial.h b/src/plugins/qmldesigner/components/materialbrowser/bundlematerial.h index ae74a13d75c..64d79aa65ee 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/bundlematerial.h +++ b/src/plugins/qmldesigner/components/materialbrowser/bundlematerial.h @@ -40,6 +40,7 @@ class BundleMaterial : public QObject Q_PROPERTY(QString bundleMaterialName MEMBER m_name CONSTANT) Q_PROPERTY(QUrl bundleMaterialIcon MEMBER m_icon CONSTANT) Q_PROPERTY(bool bundleMaterialVisible MEMBER m_visible NOTIFY materialVisibleChanged) + Q_PROPERTY(bool bundleMaterialImported READ imported WRITE setImported NOTIFY materialImportedChanged) public: BundleMaterial(QObject *parent, @@ -57,8 +58,12 @@ public: QStringList files() const; bool visible() const; + bool setImported(bool imported); + bool imported() const; + signals: void materialVisibleChanged(); + void materialImportedChanged(); private: QString m_name; @@ -68,6 +73,7 @@ private: QStringList m_files; bool m_visible = true; + bool m_imported = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/materialbrowser/bundlematerialcategory.cpp b/src/plugins/qmldesigner/components/materialbrowser/bundlematerialcategory.cpp index 9a18ff75014..716fb57dbf5 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/bundlematerialcategory.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/bundlematerialcategory.cpp @@ -37,6 +37,16 @@ void BundleMaterialCategory::addBundleMaterial(BundleMaterial *bundleMat) m_categoryMaterials.append(bundleMat); } +bool BundleMaterialCategory::updateImportedState(const QStringList &importedMats) +{ + bool changed = false; + + for (BundleMaterial *mat : std::as_const(m_categoryMaterials)) + changed |= mat->setImported(importedMats.contains(mat->qml().chopped(4))); + + return changed; +} + bool BundleMaterialCategory::filter(const QString &searchText) { bool visible = false; diff --git a/src/plugins/qmldesigner/components/materialbrowser/bundlematerialcategory.h b/src/plugins/qmldesigner/components/materialbrowser/bundlematerialcategory.h index 0a3a0f7c028..2336a03d010 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/bundlematerialcategory.h +++ b/src/plugins/qmldesigner/components/materialbrowser/bundlematerialcategory.h @@ -42,6 +42,7 @@ public: BundleMaterialCategory(QObject *parent, const QString &name); void addBundleMaterial(BundleMaterial *bundleMat); + bool updateImportedState(const QStringList &importedMats); bool filter(const QString &searchText); QString name() const; diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.cpp index 4e70008f1ab..a43a23a1d1d 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.cpp @@ -122,7 +122,7 @@ void MaterialBrowserBundleModel::loadMaterialBundle() m_matBundleExists = true; - const QString bundleId = m_matBundleObj.value("id").toString(); + QString bundleId = m_matBundleObj.value("id").toString(); const QJsonObject catsObj = m_matBundleObj.value("categories").toObject(); const QStringList categories = catsObj.keys(); @@ -160,8 +160,17 @@ void MaterialBrowserBundleModel::loadMaterialBundle() m_importer = new Internal::BundleImporter(matBundleDir.path(), bundleId, sharedFiles); connect(m_importer, &Internal::BundleImporter::importFinished, this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) { + m_importerRunning = false; + emit importerRunningChanged(); if (metaInfo.isValid()) - emit addBundleMaterialToProjectRequested(metaInfo); + emit bundleMaterialImported(metaInfo); + }); + + connect(m_importer, &Internal::BundleImporter::unimportFinished, this, [&](const QmlDesigner::NodeMetaInfo &metaInfo) { + Q_UNUSED(metaInfo) + m_importerRunning = false; + emit importerRunningChanged(); + emit bundleMaterialUnimported(metaInfo); }); } @@ -193,6 +202,11 @@ void MaterialBrowserBundleModel::setHasMaterialRoot(bool b) emit hasMaterialRootChanged(); } +Internal::BundleImporter *MaterialBrowserBundleModel::bundleImporter() const +{ + return m_importer; +} + void MaterialBrowserBundleModel::setSearchText(const QString &searchText) { QString lowerSearchText = searchText.toLower(); @@ -219,6 +233,16 @@ void MaterialBrowserBundleModel::setSearchText(const QString &searchText) resetModel(); } +void MaterialBrowserBundleModel::updateImportedState(const QStringList &importedMats) +{ + bool changed = false; + for (BundleMaterialCategory *cat : std::as_const(m_bundleCategories)) + changed |= cat->updateImportedState(importedMats); + + if (changed) + resetModel(); +} + void MaterialBrowserBundleModel::resetModel() { beginResetModel(); @@ -230,12 +254,30 @@ void MaterialBrowserBundleModel::applyToSelected(BundleMaterial *mat, bool add) emit applyToSelectedTriggered(mat, add); } -void MaterialBrowserBundleModel::addMaterial(BundleMaterial *mat) +void MaterialBrowserBundleModel::addToProject(BundleMaterial *mat) { QString err = m_importer->importComponent(mat->qml(), mat->files()); - if (!err.isEmpty()) + if (err.isEmpty()) { + m_importerRunning = true; + emit importerRunningChanged(); + } else { qWarning() << __FUNCTION__ << err; + } +} + +void MaterialBrowserBundleModel::removeFromProject(BundleMaterial *mat) +{ + emit bundleMaterialAboutToUnimport(mat->type()); + + QString err = m_importer->unimportComponent(mat->qml()); + + if (err.isEmpty()) { + m_importerRunning = true; + emit importerRunningChanged(); + } else { + qWarning() << __FUNCTION__ << err; + } } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.h index 60b48f1c9b9..8197ebd78b0 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserbundlemodel.h @@ -50,6 +50,7 @@ class MaterialBrowserBundleModel : public QAbstractListModel Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged) Q_PROPERTY(bool hasMaterialRoot READ hasMaterialRoot WRITE setHasMaterialRoot NOTIFY hasMaterialRootChanged) + Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged) public: MaterialBrowserBundleModel(QObject *parent = nullptr); @@ -59,6 +60,7 @@ public: QHash roleNames() const override; void setSearchText(const QString &searchText); + void updateImportedState(const QStringList &importedMats); bool hasQuick3DImport() const; void setHasQuick3DImport(bool b); @@ -66,10 +68,13 @@ public: bool hasMaterialRoot() const; void setHasMaterialRoot(bool b); + Internal::BundleImporter *bundleImporter() const; + void resetModel(); Q_INVOKABLE void applyToSelected(QmlDesigner::BundleMaterial *mat, bool add = false); - Q_INVOKABLE void addMaterial(QmlDesigner::BundleMaterial *mat); + Q_INVOKABLE void addToProject(QmlDesigner::BundleMaterial *mat); + Q_INVOKABLE void removeFromProject(QmlDesigner::BundleMaterial *mat); signals: void isEmptyChanged(); @@ -77,7 +82,10 @@ signals: void hasMaterialRootChanged(); void materialVisibleChanged(); void applyToSelectedTriggered(QmlDesigner::BundleMaterial *mat, bool add = false); - void addBundleMaterialToProjectRequested(const QmlDesigner::NodeMetaInfo &metaInfo); + void bundleMaterialImported(const QmlDesigner::NodeMetaInfo &metaInfo); + void bundleMaterialAboutToUnimport(const QmlDesigner::TypeName &type); + void bundleMaterialUnimported(const QmlDesigner::NodeMetaInfo &metaInfo); + void importerRunningChanged(); private: void loadMaterialBundle(); @@ -93,6 +101,7 @@ private: bool m_hasMaterialRoot = false; bool m_matBundleExists = false; bool m_probeMatBundleDir = false; + bool m_importerRunning = false; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index 94c75d93a77..ed5ab414649 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -27,6 +27,7 @@ #include "bindingproperty.h" #include "bundlematerial.h" +#include "bundleimporter.h" #include "materialbrowserwidget.h" #include "materialbrowsermodel.h" #include "materialbrowserbundlemodel.h" @@ -178,13 +179,28 @@ WidgetInfo MaterialBrowserView::widgetInfo() if (defaultMat.isValid()) applyBundleMaterialToDropTarget(defaultMat); else - m_widget->materialBrowserBundleModel()->addMaterial(bundleMat); + m_widget->materialBrowserBundleModel()->addToProject(bundleMat); }); - connect(matBrowserBundleModel, &MaterialBrowserBundleModel::addBundleMaterialToProjectRequested, this, + connect(matBrowserBundleModel, &MaterialBrowserBundleModel::bundleMaterialImported, this, [&] (const QmlDesigner::NodeMetaInfo &metaInfo) { applyBundleMaterialToDropTarget({}, metaInfo); + updateBundleMaterialsImportedState(); }); + + connect(matBrowserBundleModel, &MaterialBrowserBundleModel::bundleMaterialAboutToUnimport, this, + [&] (const QmlDesigner::TypeName &type) { + // delete instances of the bundle material that is about to be unimported + executeInTransaction("MaterialBrowserView::widgetInfo", [&] { + Utils::reverseForeach(m_widget->materialBrowserModel()->materials(), [&](const ModelNode &mat) { + if (mat.isValid() && mat.type() == type) + QmlObjectNode(mat).destroy(); + }); + }); + }); + + connect(matBrowserBundleModel, &MaterialBrowserBundleModel::bundleMaterialUnimported, this, + &MaterialBrowserView::updateBundleMaterialsImportedState); } return createWidgetInfo(m_widget.data(), @@ -273,6 +289,7 @@ void MaterialBrowserView::modelAttached(Model *model) m_widget->materialBrowserModel()->setHasMaterialRoot(rootModelNode().isSubclassOf("QtQuick3D.Material")); m_hasQuick3DImport = model->hasImport("QtQuick3D"); + updateBundleMaterialsImportedState(); // Project load is already very busy and may even trigger puppet reset, so let's wait a moment // before refreshing the model @@ -440,6 +457,22 @@ void QmlDesigner::MaterialBrowserView::loadPropertyGroups() m_propertyGroupsLoaded = m_widget->materialBrowserModel()->loadPropertyGroups(matPropsPath); } +void MaterialBrowserView::updateBundleMaterialsImportedState() +{ + using namespace Utils; + + QStringList importedBundleMats; + + FilePath materialBundlePath = m_widget->materialBrowserBundleModel()->bundleImporter()->resolveBundleImportPath(); + + if (materialBundlePath.exists()) { + importedBundleMats = transform(materialBundlePath.dirEntries({{"*.qml"}, QDir::Files}), + [](const FilePath &f) { return f.fileName().chopped(4); }); + } + + m_widget->materialBrowserBundleModel()->updateImportedState(importedBundleMats); +} + ModelNode MaterialBrowserView::getBundleMaterialDefaultInstance(const TypeName &type) { const QList materials = m_widget->materialBrowserModel()->materials(); @@ -505,7 +538,7 @@ void MaterialBrowserView::customNotification(const AbstractView *view, const QSt if (defaultMat.isValid()) applyBundleMaterialToDropTarget(defaultMat); else - m_widget->materialBrowserBundleModel()->addMaterial(m_draggedBundleMaterial); + m_widget->materialBrowserBundleModel()->addToProject(m_draggedBundleMaterial); m_draggedBundleMaterial = nullptr; } diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h index 74eaa3a6e21..8d8e225d349 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.h @@ -68,6 +68,7 @@ private: void refreshModel(bool updateImages); bool isMaterial(const ModelNode &node) const; void loadPropertyGroups(); + void updateBundleMaterialsImportedState(); void applyBundleMaterialToDropTarget(const ModelNode &bundleMat, const NodeMetaInfo &metaInfo = {}); ModelNode getBundleMaterialDefaultInstance(const TypeName &type); @@ -75,6 +76,7 @@ private: QList m_bundleMaterialTargets; QList m_selectedModels; // selected 3D model nodes BundleMaterial *m_draggedBundleMaterial = nullptr; + bool m_bundleMaterialAddToSelected = false; bool m_hasQuick3DImport = false; bool m_autoSelectModelMaterial = false; // TODO: wire this to some action From 2857c73df3ad8ccee169a5578cdd78b35b72d75b Mon Sep 17 00:00:00 2001 From: Rainer Keller Date: Mon, 17 Oct 2022 07:37:13 +0200 Subject: [PATCH 091/143] McuSupport: Hardcode the title of the groupbox The main McuPackage does not provide a title anymore. It was removed in df92e79da4a6d10ac64d011faeabf4ffbfaa9ec2. Since then, the title was empty and the second group box showed a faulty text. This change hardcodes the product name as it is not going to change very often. Task-number: QTCREATORBUG-28302 Change-Id: I6180579c9dce2f2f8aa939fb99088b4867d699fc Reviewed-by: Yasser Grimes Reviewed-by: hjk --- src/plugins/mcusupport/mcusupportoptionspage.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/mcusupport/mcusupportoptionspage.cpp b/src/plugins/mcusupport/mcusupportoptionspage.cpp index 8bdb43ef65a..328ade2ebe8 100644 --- a/src/plugins/mcusupport/mcusupportoptionspage.cpp +++ b/src/plugins/mcusupport/mcusupportoptionspage.cpp @@ -86,7 +86,7 @@ McuSupportOptionsWidget::McuSupportOptionsWidget(McuSupportOptions &options, } { - m_qtForMCUsSdkGroupBox = new QGroupBox(m_options.qtForMCUsSdkPackage->label()); + m_qtForMCUsSdkGroupBox = new QGroupBox(tr("Qt for MCUs SDK")); m_qtForMCUsSdkGroupBox->setFlat(true); auto *layout = new QVBoxLayout(m_qtForMCUsSdkGroupBox); layout->addWidget(m_options.qtForMCUsSdkPackage->widget()); @@ -94,7 +94,8 @@ McuSupportOptionsWidget::McuSupportOptionsWidget(McuSupportOptions &options, } { - m_mcuTargetsGroupBox = new QGroupBox(tr("Supported targets")); + m_mcuTargetsGroupBox = new QGroupBox( + tr("Targets supported by the %1").arg(m_qtForMCUsSdkGroupBox->title())); m_mcuTargetsGroupBox->setFlat(true); mainLayout->addWidget(m_mcuTargetsGroupBox); m_mcuTargetsComboBox = new QComboBox; From 6961790085f1736b67da933ed0eeac807919cced Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Mon, 17 Oct 2022 09:54:19 +0200 Subject: [PATCH 092/143] Utils: Fix theoretical use-after-free Change-Id: I909f2dcd71c2286036f43fce6fc8aa61c55a6695 Reviewed-by: Eike Ziller --- src/libs/utils/fsengine/filepathinfocache.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libs/utils/fsengine/filepathinfocache.h b/src/libs/utils/fsengine/filepathinfocache.h index 8b88234e3dc..5a5f2be52b9 100644 --- a/src/libs/utils/fsengine/filepathinfocache.h +++ b/src/libs/utils/fsengine/filepathinfocache.h @@ -40,7 +40,13 @@ public: if (!data) { data = new CachedData; *data = retrievalFunction(filePath); - m_cache.insert(filePath, data); + if (Q_UNLIKELY(!m_cache.insert(filePath, data))) { + // This path will never happen, but to silence coverity we + // have to check it since insert in theory could delete + // the object if a cost bigger than the cache size is + // specified. + return {}; + } } // Return a copy of the data, so it cannot be deleted by the cache From d95977db9036eabf96d63ef8d0f894b20f6e984a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20L=C3=B6hning?= Date: Fri, 14 Oct 2022 20:47:56 +0200 Subject: [PATCH 093/143] CPaster: Restore some more object names Change-Id: I590baed6d2a2ea4a8dd56f471ac7d4d0a015c725 Reviewed-by: Christian Stenger --- src/plugins/cpaster/pasteselectdialog.cpp | 4 ++++ src/plugins/cpaster/pasteview.cpp | 4 ++++ tests/system/objects.map | 10 ++++------ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/plugins/cpaster/pasteselectdialog.cpp b/src/plugins/cpaster/pasteselectdialog.cpp index 3454824fbf4..ed09368cd85 100644 --- a/src/plugins/cpaster/pasteselectdialog.cpp +++ b/src/plugins/cpaster/pasteselectdialog.cpp @@ -25,15 +25,19 @@ PasteSelectDialog::PasteSelectDialog(const QList &protocols, QWidget QDialog(parent), m_protocols(protocols) { + setObjectName("CodePaster.PasteSelectDialog"); resize(550, 350); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); m_protocolBox = new QComboBox(this); + m_protocolBox->setObjectName("protocolBox"); m_pasteEdit = new QLineEdit(this); + m_pasteEdit->setObjectName("pasteEdit"); m_pasteEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); m_listWidget = new QListWidget(this); + m_listWidget->setObjectName("listWidget"); m_listWidget->setAlternatingRowColors(true); auto buttons = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); diff --git a/src/plugins/cpaster/pasteview.cpp b/src/plugins/cpaster/pasteview.cpp index 5d3d9816ef9..e9a67370fe3 100644 --- a/src/plugins/cpaster/pasteview.cpp +++ b/src/plugins/cpaster/pasteview.cpp @@ -41,10 +41,12 @@ PasteView::PasteView(const QList &protocols, m_commentPlaceHolder(Tr::tr("")), m_mimeType(mt) { + setObjectName("CodePaster.ViewDialog"); resize(670, 678); setWindowTitle(Tr::tr("Send to Codepaster")); m_protocolBox = new QComboBox; + m_protocolBox->setObjectName("protocolBox"); for (const Protocol *p : protocols) m_protocolBox->addItem(p->name()); @@ -56,6 +58,7 @@ PasteView::PasteView(const QList &protocols, m_uiUsername->setPlaceholderText(Tr::tr("")); m_uiDescription = new QLineEdit(this); + m_uiDescription->setObjectName("uiDescription"); m_uiDescription->setPlaceholderText(Tr::tr("")); QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -83,6 +86,7 @@ PasteView::PasteView(const QList &protocols, groupBox->setFlat(true); m_plainTextEdit = new QPlainTextEdit; + m_plainTextEdit->setObjectName("plainTextEdit"); m_stackedWidget = new QStackedWidget(this); m_stackedWidget->addWidget(groupBox); diff --git a/tests/system/objects.map b/tests/system/objects.map index 5329fe8c0f9..2800960163b 100644 --- a/tests/system/objects.map +++ b/tests/system/objects.map @@ -111,7 +111,7 @@ :PasteSelectDialog.listWidget_QListWidget {name='listWidget' type='QListWidget' visible='1' window=':PasteSelectDialog_CodePaster::PasteSelectDialog'} :PasteSelectDialog.pasteEdit_QLineEdit {name='pasteEdit' type='QLineEdit' visible='1' window=':PasteSelectDialog_CodePaster::PasteSelectDialog'} :PasteSelectDialog.protocolBox_QComboBox {name='protocolBox' type='QComboBox' visible='1' window=':PasteSelectDialog_CodePaster::PasteSelectDialog'} -:PasteSelectDialog_CodePaster::PasteSelectDialog {name='CodePaster__Internal__PasteSelectDialog' type='CodePaster::PasteSelectDialog' visible='1'} +:PasteSelectDialog_CodePaster::PasteSelectDialog {name='CodePaster.PasteSelectDialog' type='QDialog' visible='1'} :Path.Utils_BaseValidatingLineEdit {container=':qt_tabwidget_stackedwidget_QWidget' name='LineEdit' type='Utils::FancyLineEdit' visible='1'} :Projects.ProjectNavigationTreeView {container=':*Qt Creator.ProjectSelectorDockWidget_QDockWidget' name='ProjectNavigation' type='Utils::BaseTreeView' visible='1'} :QML Debugging.No_QPushButton {text='No' type='QPushButton' unnamed='1' visible='1' window=':QML Debugging_QMessageBox'} @@ -187,12 +187,10 @@ :Select a Git Commit_Git::Internal::ChangeSelectionDialog {name='Git.ChangeSelectionDialog' type='Git::Internal::ChangeSelectionDialog' visible='1' windowTitle='Select a Git Commit'} :Select signal.signalList_QTreeView {container=':Go to slot.Select signal_QGroupBox' name='signalList' type='QTreeView' visible='1'} :Send to Codepaster.Cancel_QPushButton {text='Cancel' type='QPushButton' unnamed='1' visible='1' window=':Send to Codepaster_CodePaster::PasteView'} -:Send to Codepaster.Description:_QLabel {name='descriptionLabel' text='Description:' type='QLabel' visible='1' window=':Send to Codepaster_CodePaster::PasteView'} :Send to Codepaster.Paste_QPushButton {text='Paste' type='QPushButton' unnamed='1' visible='1' window=':Send to Codepaster_CodePaster::PasteView'} :Send to Codepaster.protocolBox_QComboBox {name='protocolBox' type='QComboBox' visible='1' window=':Send to Codepaster_CodePaster::PasteView'} :Send to Codepaster.qt_spinbox_lineedit_QLineEdit {name='qt_spinbox_lineedit' type='QLineEdit' visible='1' window=':Send to Codepaster_CodePaster::PasteView'} -:Send to Codepaster.stackedWidget_QStackedWidget {name='stackedWidget' type='QStackedWidget' visible='1' window=':Send to Codepaster_CodePaster::PasteView'} -:Send to Codepaster_CodePaster::PasteView {name='CodePaster__Internal__ViewDialog' type='CodePaster::PasteView' visible='1' windowTitle='Send to Codepaster'} +:Send to Codepaster_CodePaster::PasteView {name='CodePaster.ViewDialog' type='QDialog' visible='1' windowTitle='Send to Codepaster'} :Session Manager_ProjectExplorer::Internal::SessionDialog {name='ProjectExplorer__Internal__SessionDialog' type='ProjectExplorer::Internal::SessionDialog' visible='1' windowTitle='Session Manager'} :Startup.contextHelpComboBox_QComboBox {container=':Form.Startup_QGroupBox' name='contextHelpComboBox' type='QComboBox' visible='1'} :User Interface.languageBox_QComboBox {container=':Core__Internal__GeneralSettings.User Interface_QGroupBox' name='languageBox' type='QComboBox' visible='1'} @@ -222,5 +220,5 @@ :splitter.Commit File(s)_VcsBase::QActionPushButton {text~='(Commit .+/.+ File.*)' type='QToolButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :splitter.Description_QGroupBox {container=':Qt Creator.splitter_QSplitter' name='descriptionBox' title='Description' type='QGroupBox' visible='1'} :splitter.Files_QGroupBox {container=':Qt Creator.splitter_QSplitter' name='groupBox' title='Files' type='QGroupBox' visible='1'} -:stackedWidget.plainTextEdit_QPlainTextEdit {container=':Send to Codepaster.stackedWidget_QStackedWidget' name='plainTextEdit' type='QPlainTextEdit' visible='1'} -:uiDescription_QLineEdit {buddy=':Send to Codepaster.Description:_QLabel' name='uiDescription' type='QLineEdit' visible='1'} +:stackedWidget.plainTextEdit_QPlainTextEdit {name='plainTextEdit' type='QPlainTextEdit' visible='1' window=':Send to Codepaster_CodePaster::PasteView'} +:uiDescription_QLineEdit {name='uiDescription' type='QLineEdit' visible='1'} From 88837631de3499abcad678371d7f60bed9deafe3 Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 14 Oct 2022 15:57:38 +0200 Subject: [PATCH 094/143] Utils: Remove a few QFileInfo uses from FilePath::relativePath Change-Id: I0896678688cc9f7b1d635890eff0603a89f3b2e3 Reviewed-by: Marcus Tillmanns Reviewed-by: hjk --- src/libs/utils/filepath.cpp | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 90c99a48c37..d8123608cd8 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -980,28 +980,26 @@ FilePath FilePath::relativeChildPath(const FilePath &parent) const /// FilePath FilePath::relativePath(const FilePath &anchor) const { - QTC_ASSERT(!needsDevice(), return *this); + QTC_ASSERT(isSameDevice(anchor), return *this); - const QFileInfo fileInfo(toString()); - QString absolutePath; + FilePath absPath; QString filename; - if (fileInfo.isFile()) { - absolutePath = fileInfo.absolutePath(); - filename = fileInfo.fileName(); - } else if (fileInfo.isDir()) { - absolutePath = fileInfo.absoluteFilePath(); + if (isFile()) { + absPath = absolutePath(); + filename = fileName(); + } else if (isDir()) { + absPath = absoluteFilePath(); } else { return {}; } - const QFileInfo anchorInfo(anchor.toString()); - QString absoluteAnchorPath; - if (anchorInfo.isFile()) - absoluteAnchorPath = anchorInfo.absolutePath(); - else if (anchorInfo.isDir()) - absoluteAnchorPath = anchorInfo.absoluteFilePath(); + FilePath absoluteAnchorPath; + if (anchor.isFile()) + absoluteAnchorPath = anchor.absolutePath(); + else if (anchor.isDir()) + absoluteAnchorPath = anchor.absoluteFilePath(); else return {}; - QString relativeFilePath = calcRelativePath(absolutePath, absoluteAnchorPath); + QString relativeFilePath = calcRelativePath(absPath.path(), absoluteAnchorPath.path()); if (!filename.isEmpty()) { if (relativeFilePath == ".") relativeFilePath.clear(); From fdffb6c7d9a6dd22fd62c3a99b2e31a2d81d1be2 Mon Sep 17 00:00:00 2001 From: Mahmoud Badri Date: Mon, 17 Oct 2022 12:14:14 +0300 Subject: [PATCH 095/143] QmlDesigner: Fix crash when bundImporter is nullptr Change-Id: I54436b0199babe53dcaec61c74b34ed72e80ea7b Reviewed-by: Miikka Heikkinen --- .../components/materialbrowser/materialbrowserview.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp index ed5ab414649..0e13521206a 100644 --- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp +++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp @@ -461,6 +461,9 @@ void MaterialBrowserView::updateBundleMaterialsImportedState() { using namespace Utils; + if (!m_widget->materialBrowserBundleModel()->bundleImporter()) + return; + QStringList importedBundleMats; FilePath materialBundlePath = m_widget->materialBrowserBundleModel()->bundleImporter()->resolveBundleImportPath(); From bf45aa4d24f78396a98fa5e2fb4bb2261ee05355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20L=C3=B6hning?= Date: Fri, 14 Oct 2022 15:57:56 +0200 Subject: [PATCH 096/143] SquishTests: Update tst_git_clone Reverts f5f684a609cc802335456dad3868ac874008a2e1 Change-Id: Iad797f87333e8d793e34c4e7df1f68c5f6508f82 Reviewed-by: Christian Stenger --- tests/system/suite_tools/tst_git_clone/test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/system/suite_tools/tst_git_clone/test.py b/tests/system/suite_tools/tst_git_clone/test.py index 3eff4777412..01193cad52f 100644 --- a/tests/system/suite_tools/tst_git_clone/test.py +++ b/tests/system/suite_tools/tst_git_clone/test.py @@ -69,8 +69,8 @@ def verifyVersionControlView(targetDir, canceled): "Searching for target directory in clone log") test.verify(" ".join(["clone", "--progress", cloneUrl, cloneDir]) in vcsLog, "Searching for git parameters in clone log") - test.verify(canceled == (" terminated abnormally" in vcsLog), - "Searching for result in clone log") + test.compare(canceled, " terminated abnormally" in vcsLog, + "Searching for result in clone log") clickButton(waitForObject(":*Qt Creator.Clear_QToolButton")) def verifyFiles(targetDir): @@ -139,5 +139,5 @@ def main(): test.fail("The checked out project was not being opened.", str(waitForObject(":Cannot Open Project_QTextEdit").plainText)) clickButton(waitForObject(":Cannot Open Project.OK_QPushButton")) - verifyVersionControlView(targetDir, button != ":Git Repository Clone.Finish_QPushButton") + verifyVersionControlView(targetDir, button == "Cancel immediately") invokeMenuItem("File", "Exit") From a16e74fe965163c49e63f73923b18b36b75309a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20L=C3=B6hning?= Date: Fri, 14 Oct 2022 12:27:07 +0200 Subject: [PATCH 097/143] SquishTests: Fix type of Git.ChangeSelectionDialog Amends 729805e7fd0522426718da5d73c636a681c9c290 Change-Id: I79f67e5d233734ab25da1ff8a2d135061fa139e0 Reviewed-by: Christian Stenger Reviewed-by: --- tests/system/objects.map | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/objects.map b/tests/system/objects.map index 2800960163b..99635d56a52 100644 --- a/tests/system/objects.map +++ b/tests/system/objects.map @@ -184,7 +184,7 @@ :Select a Git Commit.changeNumberEdit_Utils::CompletingLineEdit {name='changeNumberEdit' type='Utils::CompletingLineEdit' visible='1' window=':Select a Git Commit_Git::Internal::ChangeSelectionDialog'} :Select a Git Commit.detailsText_QPlainTextEdit {name='detailsText' type='QPlainTextEdit' visible='1' window=':Select a Git Commit_Git::Internal::ChangeSelectionDialog'} :Select a Git Commit.workingDirectoryEdit_QLineEdit {type='Utils::FancyLineEdit' unnamed='1' visible='1' window=':Select a Git Commit_Git::Internal::ChangeSelectionDialog'} -:Select a Git Commit_Git::Internal::ChangeSelectionDialog {name='Git.ChangeSelectionDialog' type='Git::Internal::ChangeSelectionDialog' visible='1' windowTitle='Select a Git Commit'} +:Select a Git Commit_Git::Internal::ChangeSelectionDialog {name='Git.ChangeSelectionDialog' type='QDialog' visible='1' windowTitle='Select a Git Commit'} :Select signal.signalList_QTreeView {container=':Go to slot.Select signal_QGroupBox' name='signalList' type='QTreeView' visible='1'} :Send to Codepaster.Cancel_QPushButton {text='Cancel' type='QPushButton' unnamed='1' visible='1' window=':Send to Codepaster_CodePaster::PasteView'} :Send to Codepaster.Paste_QPushButton {text='Paste' type='QPushButton' unnamed='1' visible='1' window=':Send to Codepaster_CodePaster::PasteView'} From c7cef250b36223fceebd4819597297bfb7ffc68b Mon Sep 17 00:00:00 2001 From: David Schulz Date: Mon, 17 Oct 2022 11:53:23 +0200 Subject: [PATCH 098/143] Docker: fix device path mapping Amends 1fa32552425f0c3fd98ae20bfd26b8b85bfda3e0 Change-Id: Idd2e0916769cd822b97e1fc851d8f706316cca83 Reviewed-by: hjk --- src/plugins/docker/dockerdevice.cpp | 27 ++++++++++--------- src/plugins/docker/dockerdevice.h | 1 - .../projectexplorer/devicesupport/idevice.cpp | 5 ---- .../projectexplorer/devicesupport/idevice.h | 1 - 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 84a957c9719..8db1ae9ff20 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -123,6 +123,7 @@ public: RunResult runInShell(const CommandLine &cmdLine, const QByteArray &stdInData) const override; + QString mapToDevicePath(const FilePath &filePath) const override; DockerDevicePrivate *m_dev = nullptr; }; @@ -353,6 +354,19 @@ RunResult DockerDeviceFileAccess::runInShell(const CommandLine &cmdLine, return m_dev->runInShell(cmdLine, stdInData); } +QString DockerDeviceFileAccess::mapToDevicePath(const FilePath &filePath) const +{ + // make sure to convert windows style paths to unix style paths with the file system case: + // C:/dev/src -> /c/dev/src + const FilePath normalized = FilePath::fromString(filePath.path()).normalizedPathName(); + QString path = normalized.path(); + if (HostOsInfo::isWindowsHost() && normalized.startsWithDriveLetter()) { + const QChar lowerDriveLetter = path.at(0); + path = '/' + lowerDriveLetter + path.mid(2); // strip C: + } + return path; +} + DockerDevice::DockerDevice(DockerSettings *settings, const DockerDeviceData &data) : d(new DockerDevicePrivate(this, settings, data)) { @@ -774,19 +788,6 @@ FilePath DockerDevice::mapToGlobalPath(const FilePath &pathOnDevice) const // result.setHost(id().toString()); } -QString DockerDevice::mapToDevicePath(const Utils::FilePath &globalPath) const -{ - // make sure to convert windows style paths to unix style paths with the file system case: - // C:/dev/src -> /c/dev/src - const FilePath normalized = FilePath::fromString(globalPath.path()).normalizedPathName(); - QString path = normalized.path(); - if (HostOsInfo::isWindowsHost() && normalized.startsWithDriveLetter()) { - const QChar lowerDriveLetter = path.at(0).toLower(); - path = '/' + lowerDriveLetter + path.mid(2); // strip C: - } - return path; -} - Utils::FilePath DockerDevice::rootPath() const { return FilePath::fromParts(Constants::DOCKER_DEVICE_SCHEME, d->repoAndTag(), u"/"); diff --git a/src/plugins/docker/dockerdevice.h b/src/plugins/docker/dockerdevice.h index 625b45b46ff..c455185117e 100644 --- a/src/plugins/docker/dockerdevice.h +++ b/src/plugins/docker/dockerdevice.h @@ -79,7 +79,6 @@ public: bool usableAsBuildDevice() const override; Utils::FilePath mapToGlobalPath(const Utils::FilePath &pathOnDevice) const override; - QString mapToDevicePath(const Utils::FilePath &globalPath) const override; Utils::FilePath rootPath() const override; diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index 110f3f93fd0..74e8add824c 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -211,11 +211,6 @@ FilePath IDevice::mapToGlobalPath(const FilePath &pathOnDevice) const return FilePath::fromParts(u"device", id().toString(), pathOnDevice.path()); } -QString IDevice::mapToDevicePath(const FilePath &globalPath) const -{ - return globalPath.path(); -} - FilePath IDevice::filePath(const QString &pathOnDevice) const { return mapToGlobalPath(FilePath::fromString(pathOnDevice)); diff --git a/src/plugins/projectexplorer/devicesupport/idevice.h b/src/plugins/projectexplorer/devicesupport/idevice.h index 0f5649afd6c..704894b7607 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.h +++ b/src/plugins/projectexplorer/devicesupport/idevice.h @@ -219,7 +219,6 @@ public: virtual bool handlesFile(const Utils::FilePath &filePath) const; virtual Utils::FilePath mapToGlobalPath(const Utils::FilePath &pathOnDevice) const; - virtual QString mapToDevicePath(const Utils::FilePath &globalPath) const; virtual Utils::FilePath searchExecutableInPath(const QString &fileName) const; virtual Utils::FilePath searchExecutable(const QString &fileName, From bf575688808e4c8e31feb935c072f1627ef6bc02 Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 17 Oct 2022 12:09:09 +0200 Subject: [PATCH 099/143] RemoteLinux: Fix RsyncDeployService::setDeployableFiles() It previously did not set, but appended. Amends 515845b815c. Change-Id: Ida3bd66e188ad4b1bbf0ea7686ac101a3f61c28e Reviewed-by: Jarek Kobus --- src/plugins/remotelinux/rsyncdeploystep.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/remotelinux/rsyncdeploystep.cpp b/src/plugins/remotelinux/rsyncdeploystep.cpp index c6cace227ef..cfef60c80a2 100644 --- a/src/plugins/remotelinux/rsyncdeploystep.cpp +++ b/src/plugins/remotelinux/rsyncdeploystep.cpp @@ -91,6 +91,7 @@ private: void RsyncDeployService::setDeployableFiles(const QList &files) { + m_files.clear(); for (const DeployableFile &f : files) m_files.append({f.localFilePath(), deviceConfiguration()->filePath(f.remoteFilePath())}); } From 06402e0dc6405f23fac1550fa5132d048f9b32fc Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 17 Oct 2022 11:20:45 +0200 Subject: [PATCH 100/143] Re-add "Toogle Bookmark" icon into touch bar Instead of "Edit Bookmark" which was not intended to be there. Also move it behind the "navigation" items, since these are probably used more often. Amends ac47d51bfb379b8c3b51d832d54a90dba18a518f Fixes: QTCREATORBUG-28108 Change-Id: I47c443772488b7f9e29c8fadf01011d4ddfd5686 Reviewed-by: Reviewed-by: David Schulz --- src/plugins/bookmarks/bookmarksplugin.cpp | 16 ++++++---------- src/plugins/coreplugin/mainwindow.cpp | 2 +- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/plugins/bookmarks/bookmarksplugin.cpp b/src/plugins/bookmarks/bookmarksplugin.cpp index f1b6a78ddf3..a48288e39a0 100644 --- a/src/plugins/bookmarks/bookmarksplugin.cpp +++ b/src/plugins/bookmarks/bookmarksplugin.cpp @@ -88,20 +88,16 @@ BookmarksPluginPrivate::BookmarksPluginPrivate() // Toggle Command *cmd = ActionManager::registerAction(&m_toggleAction, BOOKMARKS_TOGGLE_ACTION, editorManagerContext); - cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+M") - : Tr::tr("Ctrl+M"))); + cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+M") : Tr::tr("Ctrl+M"))); cmd->setTouchBarIcon(Utils::Icons::MACOS_TOUCHBAR_BOOKMARK.icon()); mbm->addAction(cmd); - - cmd = ActionManager::registerAction(&m_editAction, - BOOKMARKS_EDIT_ACTION, - editorManagerContext); - cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Shift+M") - : Tr::tr("Ctrl+Shift+M"))); - mbm->addAction(cmd); - touchBar->addAction(cmd, Core::Constants::G_TOUCHBAR_EDITOR); + cmd = ActionManager::registerAction(&m_editAction, BOOKMARKS_EDIT_ACTION, editorManagerContext); + cmd->setDefaultKeySequence( + QKeySequence(useMacShortcuts ? Tr::tr("Meta+Shift+M") : Tr::tr("Ctrl+Shift+M"))); + mbm->addAction(cmd); + mbm->addSeparator(); // Previous diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp index a872320cafc..54c0b38a72c 100644 --- a/src/plugins/coreplugin/mainwindow.cpp +++ b/src/plugins/coreplugin/mainwindow.cpp @@ -524,8 +524,8 @@ void MainWindow::registerDefaultContainers() QIcon(), "Main TouchBar" /*never visible*/); ac->appendGroup(Constants::G_TOUCHBAR_HELP); - ac->appendGroup(Constants::G_TOUCHBAR_EDITOR); ac->appendGroup(Constants::G_TOUCHBAR_NAVIGATION); + ac->appendGroup(Constants::G_TOUCHBAR_EDITOR); ac->appendGroup(Constants::G_TOUCHBAR_OTHER); ac->touchBar()->setApplicationTouchBar(); } From bdf3c2d57669fa43bfd10a671059f41b6c99d404 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Fri, 14 Oct 2022 11:39:47 +0200 Subject: [PATCH 101/143] Squish: Slightly tweak the validation of Squish path The settings validated so far only whether the given path is an existing directory. Check at least the most important indicator whether this path can be considered valid or not. Change-Id: I01a35a4e694e309b0fd304c869b6e9d31211f38d Reviewed-by: David Schulz --- src/plugins/squish/squishsettings.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/plugins/squish/squishsettings.cpp b/src/plugins/squish/squishsettings.cpp index 58fad483e77..e763db9f80a 100644 --- a/src/plugins/squish/squishsettings.cpp +++ b/src/plugins/squish/squishsettings.cpp @@ -42,6 +42,17 @@ SquishSettings::SquishSettings() squishPath.setDisplayStyle(StringAspect::PathChooserDisplay); squishPath.setExpectedKind(PathChooser::ExistingDirectory); squishPath.setPlaceHolderText(Tr::tr("Path to Squish installation")); + squishPath.setValidationFunction([this](FancyLineEdit *edit, QString *error) { + QTC_ASSERT(edit, return false); + if (!squishPath.pathChooser()->defaultValidationFunction()(edit, error)); + return false; + const FilePath squishServer = FilePath::fromString(edit->text()) + .pathAppended(HostOsInfo::withExecutableSuffix("bin/squishserver")); + const bool valid = squishServer.isExecutableFile(); + if (!valid && error) + *error = Tr::tr("Path does not contain server executable at its default location."); + return valid; + }); registerAspect(&licensePath); licensePath.setSettingsKey("LicensePath"); From 5b3efd61c7f5de81d01b737481c9d6fc2113c303 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Mon, 17 Oct 2022 12:59:12 +0200 Subject: [PATCH 102/143] Utils: Move deviceshell script into .qrc Change-Id: I6acb542163ebe39aaea8a1f1c1b84d355f25eccc Reviewed-by: Jarek Kobus --- src/libs/utils/deviceshell.cpp | 123 +------------------------- src/libs/utils/scripts/deviceshell.sh | 116 ++++++++++++++++++++++++ src/libs/utils/utils.qrc | 1 + 3 files changed, 118 insertions(+), 122 deletions(-) create mode 100644 src/libs/utils/scripts/deviceshell.sh diff --git a/src/libs/utils/deviceshell.cpp b/src/libs/utils/deviceshell.cpp index 5e43d020219..b3133a66c14 100644 --- a/src/libs/utils/deviceshell.cpp +++ b/src/libs/utils/deviceshell.cpp @@ -14,8 +14,6 @@ Q_LOGGING_CATEGORY(deviceShellLog, "qtc.utils.deviceshell", QtWarningMsg) namespace Utils { -namespace { - /*! * The multiplex script waits for input via stdin. * @@ -31,125 +29,6 @@ namespace { * Once the process exits its exit code is send to stdout with the command-id and the type "R". * */ -const QLatin1String r_execScript = QLatin1String(R"SCRIPT( -#!/bin/sh -FINAL_OUT=$(mktemp -u) -mkfifo "$FINAL_OUT" - -finalOutput() { - local fileInputBuffer - while read fileInputBuffer - do - if test -f "$fileInputBuffer.err"; then - cat $fileInputBuffer.err - fi - cat $fileInputBuffer - rm -f $fileInputBuffer.err $fileInputBuffer - done -} - -finalOutput < $FINAL_OUT & - -readAndMark() { - local buffer - while read buffer - do - printf '%s:%s:%s\n' "$1" "$2" "$buffer" - done -} - -base64decode() -{ - base64 -d 2>/dev/null -} - -base64encode() -{ - base64 2>/dev/null -} - -executeAndMark() -{ - PID="$1" - INDATA="$2" - shift - shift - CMD="$@" - - # LogFile - TMPFILE=$(mktemp) - - # Output Streams - stdoutenc=$(mktemp -u) - stderrenc=$(mktemp -u) - mkfifo "$stdoutenc" "$stderrenc" - - # app output streams - stdoutraw=$(mktemp -u) - stderrraw=$(mktemp -u) - mkfifo "$stdoutraw" "$stderrraw" - - # Cleanup - trap 'rm -f "$stdoutenc" "$stderrenc" "$stdoutraw" "$stderrraw"' EXIT - - # Pipe all app output through base64, and then into the output streams - cat $stdoutraw | base64encode > "$stdoutenc" & - cat $stderrraw | base64encode > "$stderrenc" & - - # Mark the app's output streams - readAndMark $PID 'O' < "$stdoutenc" >> $TMPFILE & - readAndMark $PID 'E' < "$stderrenc" >> $TMPFILE.err & - - # Start the app ... - if [ -z "$INDATA" ] - then - eval $CMD 1> "$stdoutraw" 2> "$stderrraw" - else - echo $INDATA | base64decode | eval "$CMD" 1> "$stdoutraw" 2> "$stderrraw" - fi - - exitcode=$(echo $? | base64encode) - wait - echo "$PID:R:$exitcode" >> $TMPFILE - echo $TMPFILE -} - -execute() -{ - PID="$1" - - if [ "$#" -lt "3" ]; then - TMPFILE=$(mktemp) - echo "$PID:R:MjU1Cg==" > $TMPFILE - echo $TMPFILE - else - INDATA=$(eval echo "$2") - shift - shift - CMD=$@ - executeAndMark $PID "$INDATA" "$CMD" - fi -} - -cleanup() -{ - kill -- -$$ - exit 1 -} - -trap cleanup 1 2 3 6 - -echo SCRIPT_INSTALLED >&2 - -(while read -r id inData cmd; do - if [ "$id" = "exit" ]; then - exit - fi - execute $id $inData $cmd || echo "$id:R:255" & -done) > $FINAL_OUT -)SCRIPT"); - -} // namespace DeviceShell::DeviceShell(bool forceFailScriptInstallation) : m_forceFailScriptInstallation(forceFailScriptInstallation) @@ -381,7 +260,7 @@ bool DeviceShell::installShellScript() } const static QByteArray shellScriptBase64 - = QByteArray(r_execScript.begin(), r_execScript.size()).toBase64(); + = FilePath(":/utils/scripts/deviceshell.sh").fileContents().value().toBase64(); const QByteArray scriptCmd = "(scriptData=$(echo " + shellScriptBase64 + " | base64 -d 2>/dev/null ) && /bin/sh -c \"$scriptData\") || " "echo ERROR_INSTALL_SCRIPT >&2\n"; diff --git a/src/libs/utils/scripts/deviceshell.sh b/src/libs/utils/scripts/deviceshell.sh new file mode 100644 index 00000000000..86f096b256e --- /dev/null +++ b/src/libs/utils/scripts/deviceshell.sh @@ -0,0 +1,116 @@ +# 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 +FINAL_OUT=$(mktemp -u) +mkfifo "$FINAL_OUT" + +finalOutput() { + local fileInputBuffer + while read fileInputBuffer + do + if test -f "$fileInputBuffer.err"; then + cat $fileInputBuffer.err + fi + cat $fileInputBuffer + rm -f $fileInputBuffer.err $fileInputBuffer + done +} + +finalOutput < $FINAL_OUT & + +readAndMark() { + local buffer + while read buffer + do + printf '%s:%s:%s\n' "$1" "$2" "$buffer" + done +} + +base64decode() +{ + base64 -d 2>/dev/null +} + +base64encode() +{ + base64 2>/dev/null +} + +executeAndMark() +{ + PID="$1" + INDATA="$2" + shift + shift + CMD="$@" + + # LogFile + TMPFILE=$(mktemp) + + # Output Streams + stdoutenc=$(mktemp -u) + stderrenc=$(mktemp -u) + mkfifo "$stdoutenc" "$stderrenc" + + # app output streams + stdoutraw=$(mktemp -u) + stderrraw=$(mktemp -u) + mkfifo "$stdoutraw" "$stderrraw" + + # Cleanup + trap 'rm -f "$stdoutenc" "$stderrenc" "$stdoutraw" "$stderrraw"' EXIT + + # Pipe all app output through base64, and then into the output streams + cat $stdoutraw | base64encode > "$stdoutenc" & + cat $stderrraw | base64encode > "$stderrenc" & + + # Mark the app's output streams + readAndMark $PID 'O' < "$stdoutenc" >> $TMPFILE & + readAndMark $PID 'E' < "$stderrenc" >> $TMPFILE.err & + + # Start the app ... + if [ -z "$INDATA" ] + then + eval $CMD 1> "$stdoutraw" 2> "$stderrraw" + else + echo $INDATA | base64decode | eval "$CMD" 1> "$stdoutraw" 2> "$stderrraw" + fi + + exitcode=$(echo $? | base64encode) + wait + echo "$PID:R:$exitcode" >> $TMPFILE + echo $TMPFILE +} + +execute() +{ + PID="$1" + + if [ "$#" -lt "3" ]; then + TMPFILE=$(mktemp) + echo "$PID:R:MjU1Cg==" > $TMPFILE + echo $TMPFILE + else + INDATA=$(eval echo "$2") + shift + shift + CMD=$@ + executeAndMark $PID "$INDATA" "$CMD" + fi +} + +cleanup() +{ + kill -- -$$ + exit 1 +} + +trap cleanup 1 2 3 6 + +echo SCRIPT_INSTALLED >&2 + +(while read -r id inData cmd; do + if [ "$id" = "exit" ]; then + exit + fi + execute $id $inData $cmd || echo "$id:R:255" & +done) > $FINAL_OUT diff --git a/src/libs/utils/utils.qrc b/src/libs/utils/utils.qrc index 8685c9be9f2..1f2ef64898e 100644 --- a/src/libs/utils/utils.qrc +++ b/src/libs/utils/utils.qrc @@ -239,6 +239,7 @@ images/message@2x.png images/help.png ../3rdparty/xdg/freedesktop.org.xml + scripts/deviceshell.sh images/enum.png From b587110cf00ae060180da9de950493a8659b9c3a Mon Sep 17 00:00:00 2001 From: Yasser Grimes Date: Wed, 12 Oct 2022 18:23:20 +0300 Subject: [PATCH 103/143] McuSupport: Fix path to QtMCUs not changing This change will prevent creating a second fileChooser when parsing QtMCUs package. Task-number: UL-6612 Change-Id: Ib63ae77a051122ff2ba8656dedbe7ee6fd43e499 Reviewed-by: Alessandro Portale --- src/plugins/mcusupport/mcusupportoptionspage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/mcusupport/mcusupportoptionspage.cpp b/src/plugins/mcusupport/mcusupportoptionspage.cpp index 328ade2ebe8..1aa12615cc8 100644 --- a/src/plugins/mcusupport/mcusupportoptionspage.cpp +++ b/src/plugins/mcusupport/mcusupportoptionspage.cpp @@ -257,9 +257,9 @@ void McuSupportOptionsWidget::showMcuTargetPackages() } for (const auto &package : std::as_const(m_options.sdkRepository.packages)) { - QWidget *packageWidget = package->widget(); if (!mcuTarget->packages().contains(package) || package->label().isEmpty()) continue; + QWidget *packageWidget = package->widget(); m_packagesLayout->addRow(package->label(), packageWidget); packageWidget->show(); } From 0264aa2a217370176e5bae0757add6d1e80571af Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 17 Oct 2022 14:16:45 +0200 Subject: [PATCH 104/143] QtSupport: Restore some more object names Change-Id: Ia88dd8e5d3b512227f4d81e01573881afdfbe58c Reviewed-by: hjk --- src/plugins/qtsupport/qtoptionspage.cpp | 1 + tests/system/objects.map | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/qtsupport/qtoptionspage.cpp b/src/plugins/qtsupport/qtoptionspage.cpp index ada5f3c842e..16d2a2c13a2 100644 --- a/src/plugins/qtsupport/qtoptionspage.cpp +++ b/src/plugins/qtsupport/qtoptionspage.cpp @@ -236,6 +236,7 @@ QtOptionsPageWidget::QtOptionsPageWidget() resize(446, 450); m_qtdirList = new QTreeView(this); + m_qtdirList->setObjectName("qtDirList"); m_qtdirList->setUniformRowHeights(true); m_versionInfoWidget = new DetailsWidget(this); diff --git a/tests/system/objects.map b/tests/system/objects.map index 99635d56a52..da30b655651 100644 --- a/tests/system/objects.map +++ b/tests/system/objects.map @@ -92,7 +92,7 @@ :Minimal required Qt version:_QLabel {text='Minimum required Qt version:' type='QLabel' unnamed='1' visible='1' window=':New_ProjectExplorer::JsonWizard'} :New Text File.Add to project:_QLabel {name='projectLabel' text='Add to project:' type='QLabel' visible='1' window=':New_ProjectExplorer::JsonWizard'} :New Text File.nameLineEdit_Utils::FileNameValidatingLineEdit {name='nameLineEdit' type='Utils::FileNameValidatingLineEdit' visible='1' window=':New_ProjectExplorer::JsonWizard'} -:New.comboBox_QComboBox {name='comboBox' type='QComboBox' visible='1' window=':New_Core::Internal::NewDialog'} +:New.comboBox_QComboBox {type='QComboBox' unnamed='1' visible='1' window=':New_Core::Internal::NewDialog'} :New.frame_QFrame {name='frame' type='QFrame' visible='1' window=':New_Core::Internal::NewDialog'} :New.templateCategoryView_QTreeView {name='templateCategoryView' type='QTreeView' visible='1' window=':New_Core::Internal::NewDialog'} :New_Core::Internal::NewDialog {name='Core.NewDialog' type='Core::Internal::NewDialogWidget' visible='1' windowTitle?='New*'} @@ -211,7 +211,7 @@ :qt_tabwidget_stackedwidget.QtSupport__Internal__QtVersionManager_QtSupport::Internal::QtOptionsPageWidget {container=':Options.qt_tabwidget_stackedwidget_QStackedWidget' type='QScrollArea' unnamed='1' visible='1'} :qt_tabwidget_stackedwidget_QScrollArea {container=':Options.qt_tabwidget_stackedwidget_QStackedWidget' type='QScrollArea' unnamed='1' visible='1'} :qt_tabwidget_stackedwidget_QWidget {container=':Options.qt_tabwidget_stackedwidget_QStackedWidget' type='QWidget' unnamed='1' visible='1'} -:qtdirList_QTreeView {container=':qt_tabwidget_stackedwidget_QScrollArea' name='qtdirList' type='QTreeView' visible='1'} +:qtdirList_QTreeView {container=':qt_tabwidget_stackedwidget_QScrollArea' name='qtDirList' type='QTreeView' visible='1'} :scrollArea.Details_Utils::DetailsButton {text='Details' type='QToolButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :scrollArea.Edit build configuration:_QComboBox {leftWidget=':scrollArea.Edit build configuration:_QLabel' type='QComboBox' unnamed='1' visible='1'} :scrollArea.Edit build configuration:_QLabel {text='Edit build configuration:' type='QLabel' unnamed='1' visible='1'} From 7d8a140f05482c04c787c34e63eeb52b2846b303 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 17 Oct 2022 14:58:47 +0200 Subject: [PATCH 105/143] Beautifier: Fix enabling of options Set the path to clang-format after connecting with the path chooser, otherwise setting the initial enabled state breaks. Broke when inline the ui file. Amends 4933697d9a5b38299340e265d2f4e6c78dd1604a Fixes: QTCREATORBUG-28297 Change-Id: I4c6ff7c4515edf13569cf5cd125623005f2967ad Reviewed-by: hjk --- .../beautifier/clangformat/clangformatoptionspage.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/beautifier/clangformat/clangformatoptionspage.cpp b/src/plugins/beautifier/clangformat/clangformatoptionspage.cpp index fcefbfc257e..d6244eb466d 100644 --- a/src/plugins/beautifier/clangformat/clangformatoptionspage.cpp +++ b/src/plugins/beautifier/clangformat/clangformatoptionspage.cpp @@ -79,7 +79,6 @@ ClangFormatOptionsPageWidget::ClangFormatOptionsPageWidget(ClangFormatSettings * m_command->setCommandVersionArguments({"--version"}); m_command->setPromptDialogTitle( BeautifierPlugin::msgCommandPromptDialogTitle("Clang Format")); - m_command->setFilePath(m_settings->command()); if (m_settings->usePredefinedStyle()) m_usePredefinedStyle->setChecked(true); @@ -107,13 +106,16 @@ ClangFormatOptionsPageWidget::ClangFormatOptionsPageWidget(ClangFormatSettings * }.attachTo(this); connect(m_command, &Utils::PathChooser::validChanged, options, &QWidget::setEnabled); - connect(m_predefinedStyle, &QComboBox::currentTextChanged, [this](const QString &item) { + connect(m_predefinedStyle, &QComboBox::currentTextChanged, this, [this](const QString &item) { m_fallbackStyle->setEnabled(item == "File"); }); - connect(m_usePredefinedStyle, &QRadioButton::toggled, [this](bool checked) { + connect(m_usePredefinedStyle, &QRadioButton::toggled, this, [this](bool checked) { m_fallbackStyle->setEnabled(checked && m_predefinedStyle->currentText() == "File"); m_predefinedStyle->setEnabled(checked); }); + + // might trigger PathChooser::validChanged, so so after the connect above + m_command->setFilePath(m_settings->command()); } void ClangFormatOptionsPageWidget::apply() From 5b287c9210e9dc7e527644d149d4a488d6e57c43 Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 14 Oct 2022 17:13:54 +0200 Subject: [PATCH 106/143] ProjectExplorer: Avoid prefixing schemes with slash Without that I get folder nodes named "/ssh://user@12.23.45.67/..." Change-Id: I03f87c650478781fe934e8a647a8883f024b7a2b Reviewed-by: Christian Kandeler Reviewed-by: --- src/plugins/projectexplorer/projectnodes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp index 27223f066ed..1b7c0546fe7 100644 --- a/src/plugins/projectexplorer/projectnodes.cpp +++ b/src/plugins/projectexplorer/projectnodes.cpp @@ -57,8 +57,8 @@ static FolderNode *recursiveFindOrCreateFolderNode(FolderNode *folder, directoryWithoutPrefix = directory; } } - QStringList parts = directoryWithoutPrefix.toString().split('/', Qt::SkipEmptyParts); - if (!Utils::HostOsInfo::isWindowsHost() && !isRelative && !parts.isEmpty()) + QStringList parts = directoryWithoutPrefix.path().split('/', Qt::SkipEmptyParts); + if (directory.osType() != OsTypeWindows && !isRelative && !parts.isEmpty()) parts[0].prepend('/'); ProjectExplorer::FolderNode *parent = folder; From ceee8f450543c5b44e745d56a616f380788ed609 Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Mon, 17 Oct 2022 15:23:28 +0200 Subject: [PATCH 107/143] Squish: Fix if statement with empty body Change-Id: Ibf8ab419e26f896e3eabfb9c42c86ca87cd2801d Reviewed-by: Christian Stenger --- src/plugins/squish/squishsettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/squish/squishsettings.cpp b/src/plugins/squish/squishsettings.cpp index e763db9f80a..40c2c8a1539 100644 --- a/src/plugins/squish/squishsettings.cpp +++ b/src/plugins/squish/squishsettings.cpp @@ -44,7 +44,7 @@ SquishSettings::SquishSettings() squishPath.setPlaceHolderText(Tr::tr("Path to Squish installation")); squishPath.setValidationFunction([this](FancyLineEdit *edit, QString *error) { QTC_ASSERT(edit, return false); - if (!squishPath.pathChooser()->defaultValidationFunction()(edit, error)); + if (!squishPath.pathChooser()->defaultValidationFunction()(edit, error)) return false; const FilePath squishServer = FilePath::fromString(edit->text()) .pathAppended(HostOsInfo::withExecutableSuffix("bin/squishserver")); From 86beb2a9502ee2adf7462a8618c714b559f1b98b Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 17 Oct 2022 15:38:29 +0200 Subject: [PATCH 108/143] Fix translations of file properties dialog After inlining the UI file. Context changed and moved into the namespace "Core::...". German translation is updated separately. Amends 568f83d964f941ea0bbf836aaf0519d33fb9d32a Change-Id: I0540a533113f3f38ab527a6c6d8614c2699a6578 Reviewed-by: hjk --- share/qtcreator/translations/qtcreator_da.ts | 2 +- share/qtcreator/translations/qtcreator_hr.ts | 2 +- share/qtcreator/translations/qtcreator_ja.ts | 2 +- share/qtcreator/translations/qtcreator_ru.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/share/qtcreator/translations/qtcreator_da.ts b/share/qtcreator/translations/qtcreator_da.ts index df9db029fb9..0bf41f41ee6 100644 --- a/share/qtcreator/translations/qtcreator_da.ts +++ b/share/qtcreator/translations/qtcreator_da.ts @@ -41850,7 +41850,7 @@ skal være et repository krævet SSH-autentifikation (se dokumentation på SSH o - FilePropertiesDialog + Core::FilePropertiesDialog File Properties Filegenskaber diff --git a/share/qtcreator/translations/qtcreator_hr.ts b/share/qtcreator/translations/qtcreator_hr.ts index 3053522bea0..d174e99f5c5 100644 --- a/share/qtcreator/translations/qtcreator_hr.ts +++ b/share/qtcreator/translations/qtcreator_hr.ts @@ -2224,7 +2224,7 @@ Međutim, korištenje opuštenih i proširenih pravila također znači da nije m - FilePropertiesDialog + Core::FilePropertiesDialog File Properties Svojstva datoteke diff --git a/share/qtcreator/translations/qtcreator_ja.ts b/share/qtcreator/translations/qtcreator_ja.ts index 43f860bf2f1..4d63d8c72f9 100644 --- a/share/qtcreator/translations/qtcreator_ja.ts +++ b/share/qtcreator/translations/qtcreator_ja.ts @@ -45412,7 +45412,7 @@ Output: - FilePropertiesDialog + Core::FilePropertiesDialog Group: グループ: diff --git a/share/qtcreator/translations/qtcreator_ru.ts b/share/qtcreator/translations/qtcreator_ru.ts index 4ed61f06aa2..b8803fe96dc 100644 --- a/share/qtcreator/translations/qtcreator_ru.ts +++ b/share/qtcreator/translations/qtcreator_ru.ts @@ -19612,7 +19612,7 @@ will also disable the following plugins: - FilePropertiesDialog + Core::FilePropertiesDialog File Properties Свойства файла From 66b99b50860fedd2e89ed59b738e39690cb08f7d Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 17 Oct 2022 15:53:21 +0200 Subject: [PATCH 109/143] Fix translations of FolderNavigationWidget after moving to Core It moved from ProjectExplorer::Internal to Core, and there is no reason to throw the translations away. German translation is fixed separately. Amends dc5717bd0669f91c265eedf3db00a3e7217e287d Change-Id: I7a0a88c70e3454374de0b994fa1c20557c9a24e8 Reviewed-by: hjk --- share/qtcreator/translations/qtcreator_cs.ts | 4 ++-- share/qtcreator/translations/qtcreator_da.ts | 4 ++-- share/qtcreator/translations/qtcreator_es.ts | 2 +- share/qtcreator/translations/qtcreator_fr.ts | 4 ++-- share/qtcreator/translations/qtcreator_hr.ts | 4 ++-- share/qtcreator/translations/qtcreator_hu.ts | 2 +- share/qtcreator/translations/qtcreator_it.ts | 2 +- share/qtcreator/translations/qtcreator_ja.ts | 4 ++-- share/qtcreator/translations/qtcreator_pl.ts | 4 ++-- share/qtcreator/translations/qtcreator_ru.ts | 4 ++-- share/qtcreator/translations/qtcreator_sl.ts | 4 ++-- share/qtcreator/translations/qtcreator_uk.ts | 4 ++-- share/qtcreator/translations/qtcreator_zh_CN.ts | 4 ++-- share/qtcreator/translations/qtcreator_zh_TW.ts | 4 ++-- 14 files changed, 25 insertions(+), 25 deletions(-) diff --git a/share/qtcreator/translations/qtcreator_cs.ts b/share/qtcreator/translations/qtcreator_cs.ts index 3ffa42b5276..710b67ccb34 100644 --- a/share/qtcreator/translations/qtcreator_cs.ts +++ b/share/qtcreator/translations/qtcreator_cs.ts @@ -10586,7 +10586,7 @@ ve svém .pro souboru. - ProjectExplorer::Internal::FolderNavigationWidgetFactory + Core::FolderNavigationWidgetFactory File System Souborový systém @@ -25437,7 +25437,7 @@ Proces Pdb po určité době od úspěšného spuštění spadl. - ProjectExplorer::Internal::FolderNavigationWidget + Core::FolderNavigationWidget Open Otevřít diff --git a/share/qtcreator/translations/qtcreator_da.ts b/share/qtcreator/translations/qtcreator_da.ts index 0bf41f41ee6..4532a3a74dd 100644 --- a/share/qtcreator/translations/qtcreator_da.ts +++ b/share/qtcreator/translations/qtcreator_da.ts @@ -23538,7 +23538,7 @@ Ekskludering: %2 - ProjectExplorer::Internal::FolderNavigationWidget + Core::FolderNavigationWidget The file "%1" was renamed to "%2", but the following projects could not be automatically changed: %3 Filen "%1" blev omdøbt til "%2", men følgende projekter kunne ikke ændres automatisk: %3 @@ -23597,7 +23597,7 @@ Ekskludering: %2 - ProjectExplorer::Internal::FolderNavigationWidgetFactory + Core::FolderNavigationWidgetFactory File System Filsystem diff --git a/share/qtcreator/translations/qtcreator_es.ts b/share/qtcreator/translations/qtcreator_es.ts index 5db41c08c69..ca2522fe184 100644 --- a/share/qtcreator/translations/qtcreator_es.ts +++ b/share/qtcreator/translations/qtcreator_es.ts @@ -7257,7 +7257,7 @@ Nombre base de librería: %1 - ProjectExplorer::Internal::FolderNavigationWidgetFactory + Core::FolderNavigationWidgetFactory File System Sistema de archivos diff --git a/share/qtcreator/translations/qtcreator_fr.ts b/share/qtcreator/translations/qtcreator_fr.ts index 99135b4a43a..175dd0dbbfa 100644 --- a/share/qtcreator/translations/qtcreator_fr.ts +++ b/share/qtcreator/translations/qtcreator_fr.ts @@ -11314,7 +11314,7 @@ francis : voila une nouvelle suggestion :) - ProjectExplorer::Internal::FolderNavigationWidgetFactory + Core::FolderNavigationWidgetFactory File System Système de fichier @@ -25525,7 +25525,7 @@ avec un mot de passe, que vous pouvez renseigner ci-dessus. - ProjectExplorer::Internal::FolderNavigationWidget + Core::FolderNavigationWidget Open Ouvrir diff --git a/share/qtcreator/translations/qtcreator_hr.ts b/share/qtcreator/translations/qtcreator_hr.ts index d174e99f5c5..3f7108c4c98 100644 --- a/share/qtcreator/translations/qtcreator_hr.ts +++ b/share/qtcreator/translations/qtcreator_hr.ts @@ -28995,7 +28995,7 @@ Isključivo: %2 - ProjectExplorer::Internal::FolderNavigationWidget + Core::FolderNavigationWidget The file "%1" was renamed to "%2", but the following projects could not be automatically changed: %3 @@ -29657,7 +29657,7 @@ Rename %2 to %3 anyway? - ProjectExplorer::Internal::FolderNavigationWidgetFactory + Core::FolderNavigationWidgetFactory File System diff --git a/share/qtcreator/translations/qtcreator_hu.ts b/share/qtcreator/translations/qtcreator_hu.ts index 9f2faeca741..f7c0857fc8c 100644 --- a/share/qtcreator/translations/qtcreator_hu.ts +++ b/share/qtcreator/translations/qtcreator_hu.ts @@ -11744,7 +11744,7 @@ Ok: %2 - ProjectExplorer::Internal::FolderNavigationWidgetFactory + Core::FolderNavigationWidgetFactory File System Fájlrendszer diff --git a/share/qtcreator/translations/qtcreator_it.ts b/share/qtcreator/translations/qtcreator_it.ts index b475a4a7235..bbbd0173674 100644 --- a/share/qtcreator/translations/qtcreator_it.ts +++ b/share/qtcreator/translations/qtcreator_it.ts @@ -7190,7 +7190,7 @@ Nome di base della libreria: %1 - ProjectExplorer::Internal::FolderNavigationWidgetFactory + Core::FolderNavigationWidgetFactory File System File System diff --git a/share/qtcreator/translations/qtcreator_ja.ts b/share/qtcreator/translations/qtcreator_ja.ts index 4d63d8c72f9..7f212bbe547 100644 --- a/share/qtcreator/translations/qtcreator_ja.ts +++ b/share/qtcreator/translations/qtcreator_ja.ts @@ -24100,7 +24100,7 @@ Excluding: %2 - ProjectExplorer::Internal::FolderNavigationWidget + Core::FolderNavigationWidget The file "%1" was renamed to "%2", but the following projects could not be automatically changed: %3 ファイル "%1" が "%2" に名前変更されましたが、以下のプロジェクトは自動的に変更できませんでした:"%3" @@ -24175,7 +24175,7 @@ Excluding: %2 - ProjectExplorer::Internal::FolderNavigationWidgetFactory + Core::FolderNavigationWidgetFactory File System ファイルシステム diff --git a/share/qtcreator/translations/qtcreator_pl.ts b/share/qtcreator/translations/qtcreator_pl.ts index 0538eed8015..611cdb586c5 100644 --- a/share/qtcreator/translations/qtcreator_pl.ts +++ b/share/qtcreator/translations/qtcreator_pl.ts @@ -7437,7 +7437,7 @@ Wykluczenia: %2 - ProjectExplorer::Internal::FolderNavigationWidgetFactory + Core::FolderNavigationWidgetFactory File System System plików @@ -11577,7 +11577,7 @@ Dla projektów CMake, upewnij się, że zmienna QML_IMPORT_PATH jest obecna w CM - ProjectExplorer::Internal::FolderNavigationWidget + Core::FolderNavigationWidget Open Otwórz diff --git a/share/qtcreator/translations/qtcreator_ru.ts b/share/qtcreator/translations/qtcreator_ru.ts index b8803fe96dc..21f3707a4ca 100644 --- a/share/qtcreator/translations/qtcreator_ru.ts +++ b/share/qtcreator/translations/qtcreator_ru.ts @@ -30461,7 +30461,7 @@ What should Qt Creator do now? - ProjectExplorer::Internal::FolderNavigationWidget + Core::FolderNavigationWidget Open "%1" Открыть «%1» @@ -30520,7 +30520,7 @@ What should Qt Creator do now? - ProjectExplorer::Internal::FolderNavigationWidgetFactory + Core::FolderNavigationWidgetFactory File System Файловая система diff --git a/share/qtcreator/translations/qtcreator_sl.ts b/share/qtcreator/translations/qtcreator_sl.ts index 753a55b51f6..7f4ef186521 100644 --- a/share/qtcreator/translations/qtcreator_sl.ts +++ b/share/qtcreator/translations/qtcreator_sl.ts @@ -7499,7 +7499,7 @@ enojen »Vstopi« za oddajo signala pa vas bo privedel neposredno do ustrezne pr - ProjectExplorer::Internal::FolderNavigationWidgetFactory + Core::FolderNavigationWidgetFactory File System Datotečni sistem @@ -16530,7 +16530,7 @@ Desetiška predznačena vrednost (najprej veliki konec): %4 - ProjectExplorer::Internal::FolderNavigationWidget + Core::FolderNavigationWidget Open Odpri diff --git a/share/qtcreator/translations/qtcreator_uk.ts b/share/qtcreator/translations/qtcreator_uk.ts index e86e108f6e3..7ae1def1d11 100644 --- a/share/qtcreator/translations/qtcreator_uk.ts +++ b/share/qtcreator/translations/qtcreator_uk.ts @@ -14857,7 +14857,7 @@ Reason: %2 - ProjectExplorer::Internal::FolderNavigationWidget + Core::FolderNavigationWidget Open Відкрити @@ -14904,7 +14904,7 @@ Reason: %2 - ProjectExplorer::Internal::FolderNavigationWidgetFactory + Core::FolderNavigationWidgetFactory File System Файлова система diff --git a/share/qtcreator/translations/qtcreator_zh_CN.ts b/share/qtcreator/translations/qtcreator_zh_CN.ts index d28eabcb22d..551984479a6 100644 --- a/share/qtcreator/translations/qtcreator_zh_CN.ts +++ b/share/qtcreator/translations/qtcreator_zh_CN.ts @@ -10454,7 +10454,7 @@ SOURCES *= .../ide/main/bin/dumper/dumper.cpp - ProjectExplorer::Internal::FolderNavigationWidgetFactory + Core::FolderNavigationWidgetFactory File System 文件系统 @@ -22789,7 +22789,7 @@ Previous decimal signed value (big endian): %4 - ProjectExplorer::Internal::FolderNavigationWidget + Core::FolderNavigationWidget Open 打开 diff --git a/share/qtcreator/translations/qtcreator_zh_TW.ts b/share/qtcreator/translations/qtcreator_zh_TW.ts index f2d9c06bfa3..1b0160912b6 100644 --- a/share/qtcreator/translations/qtcreator_zh_TW.ts +++ b/share/qtcreator/translations/qtcreator_zh_TW.ts @@ -6939,7 +6939,7 @@ Add, modify, and remove document filters, which determine the documentation set - ProjectExplorer::Internal::FolderNavigationWidgetFactory + Core::FolderNavigationWidgetFactory File System 檔案系統 @@ -13486,7 +13486,7 @@ For qmlproject projects, use the importPaths property to add import paths. - ProjectExplorer::Internal::FolderNavigationWidget + Core::FolderNavigationWidget Open 開啟 From ac28843c3ae043042a3b1f57de28304dd0cae10f Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Tue, 11 Oct 2022 15:07:36 +0300 Subject: [PATCH 110/143] Themes: Use lighter link colors in dark themes Fixes: QTCREATORBUG-28152 Change-Id: Id387d20373a3e19a36af9ec8f8c44798e1008ed1 Reviewed-by: Alessandro Portale --- share/qtcreator/themes/dark.creatortheme | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/themes/dark.creatortheme b/share/qtcreator/themes/dark.creatortheme index b762ccdcc97..5f721a548d9 100644 --- a/share/qtcreator/themes/dark.creatortheme +++ b/share/qtcreator/themes/dark.creatortheme @@ -16,8 +16,8 @@ alternateBackground=ff515151 error=ffd84044 warning=ffe0b716 splitterColor=ff313131 -textColorLink=ff007af4 -textColorLinkVisited=ffa57aff +textColorLink=ff8ab4f8 +textColorLinkVisited=ffc58af9 backgroundColorDisabled=ff444444 qmlDesignerButtonColor=ff3c3e40 From cb1ca97deddf2deefbcdce5a777574380156943c Mon Sep 17 00:00:00 2001 From: Rainer Keller Date: Mon, 17 Oct 2022 09:46:39 +0200 Subject: [PATCH 111/143] McuSupport: Set initial parents for created widgets Change-Id: I2e1575e848b7480dbabf1193fccfba2596c0f431 Reviewed-by: Reviewed-by: Yasser Grimes Reviewed-by: hjk --- src/plugins/mcusupport/mcupackage.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/mcusupport/mcupackage.cpp b/src/plugins/mcusupport/mcupackage.cpp index fbe9a015f34..93310399ef1 100644 --- a/src/plugins/mcusupport/mcupackage.cpp +++ b/src/plugins/mcusupport/mcupackage.cpp @@ -227,7 +227,7 @@ bool McuPackage::writeToSettings() const QWidget *McuPackage::widget() { auto *widget = new QWidget; - m_fileChooser = new PathChooser; + m_fileChooser = new PathChooser(widget); m_fileChooser->lineEdit()->setButtonIcon(FancyLineEdit::Right, Icons::RESET.icon()); m_fileChooser->lineEdit()->setButtonVisible(FancyLineEdit::Right, true); connect(m_fileChooser->lineEdit(), &FancyLineEdit::rightButtonClicked, this, [&] { @@ -236,10 +236,10 @@ QWidget *McuPackage::widget() auto layout = new QGridLayout(widget); layout->setContentsMargins(0, 0, 0, 0); - m_infoLabel = new InfoLabel(); + m_infoLabel = new InfoLabel(widget); if (!m_downloadUrl.isEmpty()) { - auto downLoadButton = new QToolButton; + auto downLoadButton = new QToolButton(widget); downLoadButton->setIcon(Icons::ONLINE.icon()); downLoadButton->setToolTip(tr("Download from \"%1\"").arg(m_downloadUrl)); QObject::connect(downLoadButton, &QToolButton::pressed, this, [this] { From 3499cd79c7397da718f2e0d1ef34f1b4dcab2485 Mon Sep 17 00:00:00 2001 From: Rainer Keller Date: Mon, 17 Oct 2022 08:16:47 +0200 Subject: [PATCH 112/143] McuSupport: Delete unused widgets The takeRow() function does not delete any widget. New rows are inserted using newly created widgets. This results in a memory leak of hidden widgets. This change deletes the widgets when removing them from the layout. Change-Id: Idda877c3cf5c20fc40bfbedb26da11270752e821 Reviewed-by: Yasser Grimes Reviewed-by: hjk --- src/plugins/mcusupport/mcusupportoptionspage.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/plugins/mcusupport/mcusupportoptionspage.cpp b/src/plugins/mcusupport/mcusupportoptionspage.cpp index 1aa12615cc8..4258b816d2d 100644 --- a/src/plugins/mcusupport/mcusupportoptionspage.cpp +++ b/src/plugins/mcusupport/mcusupportoptionspage.cpp @@ -251,9 +251,7 @@ void McuSupportOptionsWidget::showMcuTargetPackages() return; while (m_packagesLayout->rowCount() > 0) { - QFormLayout::TakeRowResult row = m_packagesLayout->takeRow(0); - row.labelItem->widget()->hide(); - row.fieldItem->widget()->hide(); + m_packagesLayout->removeRow(0); } for (const auto &package : std::as_const(m_options.sdkRepository.packages)) { From 742f92d246afc7733b260f11052e538ab47604c2 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 14 Oct 2022 10:01:46 +0200 Subject: [PATCH 113/143] Docker: Use host.docker.internal DockerDesktop on Windows and macOS provide the hostname host.docker.internal to reach the host machine. We use that as its more likely to work for different setups than trying to figure out the host ip yourself. Change-Id: I9f594d0e97e7f35ceb580ac3e3304de999bfea9c Reviewed-by: hjk Reviewed-by: David Schulz Reviewed-by: --- src/plugins/docker/dockerdevice.cpp | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index 8db1ae9ff20..fdc367e671b 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -479,20 +479,6 @@ void DockerDevicePrivate::stopCurrentContainer() m_cachedEnviroment.clear(); } -static QString getLocalIPv4Address() -{ - const QList addresses = QNetworkInterface::allAddresses(); - for (auto &a : addresses) { - if (a.isInSubnet(QHostAddress("192.168.0.0"), 16)) - return a.toString(); - if (a.isInSubnet(QHostAddress("10.0.0.0"), 8)) - return a.toString(); - if (a.isInSubnet(QHostAddress("172.16.0.0"), 12)) - return a.toString(); - } - return QString(); -} - bool DockerDevicePrivate::prepareForBuild(const Target *target) { QTC_ASSERT(QThread::currentThread() == thread(), return false); @@ -574,7 +560,7 @@ bool DockerDevicePrivate::createContainer() return false; const QString display = HostOsInfo::isLinuxHost() ? QString(":0") - : QString(getLocalIPv4Address() + ":0.0"); + : QString("host.docker.internal:0"); CommandLine dockerCreate{m_settings->dockerBinaryPath.filePath(), {"create", "-i", From a0e87e458b5af22a81a87f6fe5f3e0775ed8b697 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 17 Oct 2022 10:35:20 +0300 Subject: [PATCH 114/143] Sections could be non-collapsible A property named collapsible is considered for Sections. The default value is true for this property. The section would be affected by the user collapse request if the collapsible is true (Single collapse by clicking, and colapseAll request). Otherwise, the section will not be affected by user requests. Task-number: QDS-7527 Change-Id: Ic4c9d0489d4b8c9ec90a0c71c755fa4c52935d39 Reviewed-by: Miikka Heikkinen Reviewed-by: Mahmoud Badri --- .../MaterialEditorTopSection.qml | 1 + .../imports/HelperWidgets/Section.qml | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml index afc4be51866..6aaad4eaa13 100644 --- a/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml +++ b/share/qtcreator/qmldesigner/materialEditorQmlSources/MaterialEditorTopSection.qml @@ -197,6 +197,7 @@ Column { // 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") } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml index 143ec349d04..69cf7c77953 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml @@ -51,6 +51,7 @@ Item { property int level: 0 property int levelShift: 10 property bool hideHeader: false + property bool collapsible: true property bool expandOnClick: true // if false, toggleExpand signal will be emitted instead property bool addTopPadding: true property bool addBottomPadding: true @@ -70,7 +71,10 @@ Item { Connections { target: Controller - function onCollapseAll() { section.expanded = false } + function onCollapseAll() { + if (collapsible) + section.expanded = false + } function onExpandAll() { section.expanded = true } } @@ -142,6 +146,9 @@ Item { acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: function(mouse) { if (mouse.button === Qt.LeftButton) { + if (!section.collapsible && section.expanded) + return + transition.enabled = true if (section.expandOnClick) section.expanded = !section.expanded From 88c756258244648c555cd6bb77c3bb4ab53f01d1 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Tue, 11 Oct 2022 17:47:20 +0300 Subject: [PATCH 115/143] QmlDesigner: Fix the position of placeholder in FilterComboBox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AutoCompleteString view had a bad position. The text property of the TextMetrics was wrong, which caused the placeholder to have bad position since the position was calculated with wrong text. In addition, I prevented showing the placeholder when the root comboBox is not opened, because otherwise it may show text when no item is selected. The main task was QDS-7662, which is now divided. Task-number: QDS-7912 Change-Id: Ib937d923191ec3c544dae3259774cd2bb5b7adb0 Reviewed-by: Henning Gründl --- .../imports/StudioControls/FilterComboBox.qml | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml index da0850fb14a..fd9ddcc445a 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml @@ -547,20 +547,27 @@ Item { ] Text { - visible: root.autocompleteString !== "" + id: tmpSelectionName + visible: root.autocompleteString !== "" && root.open text: root.autocompleteString x: textInput.leftPadding + textMetrics.advanceWidth - y: (textInput.height - Math.ceil(textMetrics.height)) / 2 + y: (textInput.height - Math.ceil(tmpSelectionTextMetrics.height)) / 2 color: "gray" // TODO proper color value font: textInput.font renderType: textInput.renderType + + TextMetrics { + id: textMetrics + font: textInput.font + text: textInput.text + } + TextMetrics { + id: tmpSelectionTextMetrics + font: tmpSelectionName.font + text: "Xq" + } } - TextMetrics { - id: textMetrics - font: textInput.font - text: textInput.text - } Rectangle { id: checkIndicator From 8aaf60218c5bb3952687f749dbcdcf48ec47d046 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Fri, 14 Oct 2022 15:14:01 +0200 Subject: [PATCH 116/143] Doc: Add alternative terms for "shadow build" to the glossary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I36e160840520778f5ef7012ef72fb1abb967a3c5 Reviewed-by: Jörg Bornemann Reviewed-by: --- doc/qtcreator/src/overview/creator-only/creator-glossary.qdoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/qtcreator/src/overview/creator-only/creator-glossary.qdoc b/doc/qtcreator/src/overview/creator-only/creator-glossary.qdoc index bf8bdcad611..43959315b4e 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-glossary.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-glossary.qdoc @@ -125,6 +125,10 @@ if you need many build configurations for a single set of source files. + Shadow builds are also widely known as \e {out-of-source builds} + to separate them from \e {in-source builds} or + \e {in-tree builds}. + \endtable */ From 31e24208b50a54682c71b547ff19a38d0e373c2c Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Fri, 14 Oct 2022 16:24:44 +0200 Subject: [PATCH 117/143] Doc: Describe using CMake presets for configuring and building Update screenshots. Task-number: QTCREATORBUG-27876 Change-Id: I62135a0254b730b2cbbad65f5bac7b1594ee454e Reviewed-by: Cristian Adam --- ...qtcreator-cmake-build-settings-initial.png | Bin 22319 -> 24618 bytes .../images/qtcreator-cmake-build-settings.png | Bin 22868 -> 21459 bytes .../creator-projects-cmake-building.qdoc | 13 +++++++++++++ 3 files changed, 13 insertions(+) diff --git a/doc/qtcreator/images/qtcreator-cmake-build-settings-initial.png b/doc/qtcreator/images/qtcreator-cmake-build-settings-initial.png index ca0dbd741b3f9100aa713bc1c5241b19e95a9a7c..3392c8ee6715261062e03b88509c933416e61c9f 100644 GIT binary patch literal 24618 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 literal 22319 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 SOkrSPVDNPHb6Mw<&;$T!X^;8< diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc b/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc index 8c12817c211..2e8d893c197 100644 --- a/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc +++ b/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdoc @@ -47,6 +47,19 @@ Select \uicontrol {Kit Configuration} to edit the CMake settings for the build and run kit selected for the project. + \section CMake Presets + + You can use CMake presets files to specify common configure, build, and test + options and share them with others. \c CMakePresets.json contains options for + project-wide builds, whereas \c CMakeUserPresets.json contains options for + your local builds. + + Create the presets files in the format described in + \l{https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html} + {cmake-presets(7)} and store the in project's root directory. + + To use the preset files, specify the \c {--preset} option for your project. + \section1 Multi-Config Support \QC supports From ef476e538f8be912574425ae2777153961e9be5d Mon Sep 17 00:00:00 2001 From: David Schulz Date: Tue, 18 Oct 2022 08:36:17 +0200 Subject: [PATCH 118/143] Utils: replace windows line breaks with unix breaks in deviceshell.sh Change-Id: I6e07497370360ff04dfec66b55c2e69c0215d235 Reviewed-by: hjk --- src/libs/utils/deviceshell.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libs/utils/deviceshell.cpp b/src/libs/utils/deviceshell.cpp index b3133a66c14..9dc4633e957 100644 --- a/src/libs/utils/deviceshell.cpp +++ b/src/libs/utils/deviceshell.cpp @@ -259,8 +259,11 @@ bool DeviceShell::installShellScript() return false; } - const static QByteArray shellScriptBase64 - = FilePath(":/utils/scripts/deviceshell.sh").fileContents().value().toBase64(); + const static QByteArray shellScriptBase64 = FilePath(":/utils/scripts/deviceshell.sh") + .fileContents() + .value() + .replace("\r\n", "\n") + .toBase64(); const QByteArray scriptCmd = "(scriptData=$(echo " + shellScriptBase64 + " | base64 -d 2>/dev/null ) && /bin/sh -c \"$scriptData\") || " "echo ERROR_INSTALL_SCRIPT >&2\n"; From 89e90f31f1b8615f912bba9ba62b44dad7e310c2 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Mon, 17 Oct 2022 12:34:38 +0200 Subject: [PATCH 119/143] Python: Do not remove unreachable interpreter on startup A device might be not reachable at Qt Creator startup so do not filter out remote python interpreters after loading the settings. Change-Id: I3ac4f2baaca882e699f2210f6f5a92523891b0ca Reviewed-by: Reviewed-by: Christian Stenger --- src/plugins/python/pythonsettings.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/python/pythonsettings.cpp b/src/plugins/python/pythonsettings.cpp index bc9a906e493..313e7db70de 100644 --- a/src/plugins/python/pythonsettings.cpp +++ b/src/plugins/python/pythonsettings.cpp @@ -835,9 +835,13 @@ void PythonSettings::initFromSettings(QSettings *settings) m_interpreters << interpreter; } - m_interpreters = Utils::filtered(m_interpreters, [](const Interpreter &interpreter){ - return !interpreter.autoDetected || interpreter.command.isExecutableFile(); - }); + const auto keepInterpreter = [](const Interpreter &interpreter) { + return !interpreter.autoDetected // always keep user added interpreters + || interpreter.command.needsDevice() // remote devices might not be reachable at startup + || interpreter.command.isExecutableFile(); + }; + + m_interpreters = Utils::filtered(m_interpreters, keepInterpreter); m_defaultInterpreterId = settings->value(defaultKey).toString(); From 2e8d2e361cc81a8b402aff7919b5981b79f3d7f2 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Thu, 13 Oct 2022 09:56:49 +0200 Subject: [PATCH 120/143] ProjectExplorer: skip cmd autorun for msvc detection 3rd party application like clink can slow down the execution of msvc env script drasticly. And especially the vcvars script that collects the msvc environment modifications should be checked without any additional user modifications. Task-number: QTCREATORBUG-27906 Change-Id: I7393a3e7e367a26408e52a419ccba75ed8e3cad0 Reviewed-by: Cristian Adam Reviewed-by: --- src/plugins/projectexplorer/msvctoolchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index b320e474f41..125eb269722 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -2117,7 +2117,7 @@ std::optional MsvcToolChain::generateEnvironmentSettings(const Utils::E if (cmdPath.isEmpty()) cmdPath = env.searchInPath(QLatin1String("cmd.exe")); // Windows SDK setup scripts require command line switches for environment expansion. - CommandLine cmd(cmdPath, {"/E:ON", "/V:ON", "/c", saver.filePath().toUserOutput()}); + CommandLine cmd(cmdPath, {"/D", "/E:ON", "/V:ON", "/c", saver.filePath().toUserOutput()}); qCDebug(Log) << "readEnvironmentSetting: " << call << cmd.toUserOutput() << " Env: " << runEnv.toStringList().size(); run.setCodec(QTextCodec::codecForName("UTF-8")); From e328c90f4c1e4d00afb2bb68c365cfcb10e9085b Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Mon, 17 Oct 2022 13:21:08 +0200 Subject: [PATCH 121/143] Utils: Fix code-line lengths Change-Id: Idc273690600bfbefe9b5913e29c6ac5ea30f4e08 Reviewed-by: hjk --- src/libs/utils/deviceshell.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libs/utils/deviceshell.cpp b/src/libs/utils/deviceshell.cpp index 9dc4633e957..4cd8642ec0f 100644 --- a/src/libs/utils/deviceshell.cpp +++ b/src/libs/utils/deviceshell.cpp @@ -17,14 +17,17 @@ namespace Utils { /*! * The multiplex script waits for input via stdin. * - * To start a command, a message is send with the format " "" \n" + * To start a command, a message is send with + * the format " "" \n" * To stop the script, simply send "exit\n" via stdin * - * Once a message is received, two new streams are created that the new process redirects its output to ( $stdoutraw and $stderrraw ). + * Once a message is received, two new streams are created that the new process redirects + * its output to ( $stdoutraw and $stderrraw ). * * These streams are piped through base64 into the two streams stdoutenc and stderrenc. * - * Two subshells read from these base64 encoded streams, and prepend the command-id, as well as either "O:" or "E:" depending on whether its the stdout or stderr stream. + * Two subshells read from these base64 encoded streams, and prepend the command-id, + * as well as either "O:" or "E:" depending on whether its the stdout or stderr stream. * * Once the process exits its exit code is send to stdout with the command-id and the type "R". * From e83a6bacb67557c8e2a5c315d7aadea185ba8f06 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 14 Oct 2022 09:48:23 +0200 Subject: [PATCH 122/143] Utils: Fix pathchooser for remote directories PathChooserPrivate::expandedPath has a hack that interferes with remote paths when they are already absolute. This patch short-circuits it to keep absolute remote paths intact. Change-Id: I6438842a693653404aa07160de9f68bdbf22519b Reviewed-by: hjk --- src/libs/utils/pathchooser.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp index 3d7ac909740..ef30703c9b7 100644 --- a/src/libs/utils/pathchooser.cpp +++ b/src/libs/utils/pathchooser.cpp @@ -199,6 +199,9 @@ FilePath PathChooserPrivate::expandedPath(const FilePath &input) const if (path.isEmpty()) return path; + if (path.isAbsolutePath()) + return path; + switch (m_acceptingKind) { case PathChooser::Command: case PathChooser::ExistingCommand: { @@ -391,7 +394,7 @@ void PathChooser::slotBrowse() case PathChooser::ExistingDirectory: newPath = FileUtils::getExistingDirectory(this, makeDialogTitle(tr("Choose Directory")), - predefined); + predefined, {}, d->m_allowPathFromDevice); break; case PathChooser::ExistingCommand: case PathChooser::Command: From fdf1baecd627c48370df1687d2c486c154d4aefd Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 14 Oct 2022 12:53:01 +0300 Subject: [PATCH 123/143] QmlDesigner: Create asset_imports directory at project creation Some watchers won't be initialized correctly if the folder doesn't exist at project startup. E.g. texture images won't appear in project view when material with textures is imported, unless asset_imports already exists at project load. Task-number: QDS-7813 Change-Id: I79449ae823d9005e74594f08bfa87abf44045caf Reviewed-by: Thomas Hartmann Reviewed-by: Reviewed-by: Eike Ziller --- .../studio_templates/projects/application-3d/wizard.json | 4 ++++ .../studio_templates/projects/application/wizard.json | 4 ++++ .../studio_templates/projects/common/asset_imports.txt | 1 + .../studio_templates/projects/desktop-launcher/wizard.json | 4 ++++ .../studio_templates/projects/mobile-scroll/wizard.json | 4 ++++ .../studio_templates/projects/mobile-stack/wizard.json | 4 ++++ .../studio_templates/projects/mobile-swipe/wizard.json | 4 ++++ 7 files changed, 25 insertions(+) create mode 100644 share/qtcreator/qmldesigner/studio_templates/projects/common/asset_imports.txt diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json index 9d60367997a..3510d3e190b 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application-3d/wizard.json @@ -328,6 +328,10 @@ "source": "../common/fonts.txt", "target": "%{ProjectDirectory}/content/fonts/fonts.txt" }, + { + "source": "../common/asset_imports.txt", + "target": "%{ProjectDirectory}/asset_imports/asset_imports.txt" + }, { "source": "../common/CMakeLists.imports.txt.tpl", "target": "%{ProjectDirectory}/imports/CMakeLists.txt" diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json index 6912200c14b..68ffb6cff17 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/application/wizard.json @@ -324,6 +324,10 @@ "source": "../common/fonts.txt", "target": "%{ProjectDirectory}/content/fonts/fonts.txt" }, + { + "source": "../common/asset_imports.txt", + "target": "%{ProjectDirectory}/asset_imports/asset_imports.txt" + }, { "source": "../common/CMakeLists.imports.txt.tpl", "target": "%{ProjectDirectory}/imports/CMakeLists.txt" diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/common/asset_imports.txt b/share/qtcreator/qmldesigner/studio_templates/projects/common/asset_imports.txt new file mode 100644 index 00000000000..84c843f100d --- /dev/null +++ b/share/qtcreator/qmldesigner/studio_templates/projects/common/asset_imports.txt @@ -0,0 +1 @@ +Imported 3D assets and components imported from bundles will be created in this folder. diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json index 20a3baba9c7..ab1830752b1 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/desktop-launcher/wizard.json @@ -323,6 +323,10 @@ "source": "../common/fonts.txt", "target": "%{ProjectDirectory}/content/fonts/fonts.txt" }, + { + "source": "../common/asset_imports.txt", + "target": "%{ProjectDirectory}/asset_imports/asset_imports.txt" + }, { "source": "../common/CMakeLists.imports.txt.tpl", "target": "%{ProjectDirectory}/imports/CMakeLists.txt" diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json index 3117c7e0544..1a9e07cb63c 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-scroll/wizard.json @@ -280,6 +280,10 @@ "source": "../common/fonts.txt", "target": "%{ProjectDirectory}/content/fonts/fonts.txt" }, + { + "source": "../common/asset_imports.txt", + "target": "%{ProjectDirectory}/asset_imports/asset_imports.txt" + }, { "source": "../common/CMakeLists.imports.txt.tpl", "target": "%{ProjectDirectory}/imports/CMakeLists.txt" diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json index cb083e84f4b..2e07c4725a1 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-stack/wizard.json @@ -284,6 +284,10 @@ "source": "../common/fonts.txt", "target": "%{ProjectDirectory}/content/fonts/fonts.txt" }, + { + "source": "../common/asset_imports.txt", + "target": "%{ProjectDirectory}/asset_imports/asset_imports.txt" + }, { "source": "../common/CMakeLists.imports.txt.tpl", "target": "%{ProjectDirectory}/imports/CMakeLists.txt" diff --git a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json index 1c1137afddb..422db173497 100644 --- a/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json +++ b/share/qtcreator/qmldesigner/studio_templates/projects/mobile-swipe/wizard.json @@ -284,6 +284,10 @@ "source": "../common/fonts.txt", "target": "%{ProjectDirectory}/content/fonts/fonts.txt" }, + { + "source": "../common/asset_imports.txt", + "target": "%{ProjectDirectory}/asset_imports/asset_imports.txt" + }, { "source": "../common/CMakeLists.imports.txt.tpl", "target": "%{ProjectDirectory}/imports/CMakeLists.txt" From 6276f5514da089c6f56d6746bd56717f162a8041 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 17 Oct 2022 11:44:07 +0300 Subject: [PATCH 124/143] QmlDesigner: Add QML/QtObjectPane.qml In Qt 6.4 QtObject module resolves as QML, so add corresponding property pane. Fixes: QDS-7059 Change-Id: I115bd871132839dc170add254055022840176989 Reviewed-by: Reviewed-by: Mahmoud Badri Reviewed-by: Thomas Hartmann --- .../QML/QtObjectPane.qml | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 share/qtcreator/qmldesigner/propertyEditorQmlSources/QML/QtObjectPane.qml diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QML/QtObjectPane.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QML/QtObjectPane.qml new file mode 100644 index 00000000000..cac322fcf83 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QML/QtObjectPane.qml @@ -0,0 +1,29 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +import QtQuick 2.15 +import "../QtQuick" as Original + +Original.QtObjectPane {} From 570a2f75ea757c47cab30dababbdcb812235f010 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 14 Oct 2022 09:44:46 +0200 Subject: [PATCH 125/143] Utils: Check shift pressed for directory chooser Change-Id: I69e9ff2accf51ad7f6327b6708f114a0f87ada42 Reviewed-by: Christian Kandeler --- src/libs/utils/fileutils.cpp | 9 ++++++++- src/libs/utils/fileutils.h | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index ce5ac3a2497..6715ff731b6 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -534,10 +534,17 @@ FilePath FileUtils::getSaveFilePath(QWidget *parent, FilePath FileUtils::getExistingDirectory(QWidget *parent, const QString &caption, const FilePath &dir, - QFileDialog::Options options) + QFileDialog::Options options, + bool fromDeviceIfShiftIsPressed) { bool forceNonNativeDialog = dir.needsDevice(); +#ifdef QT_GUI_LIB + if (fromDeviceIfShiftIsPressed && qApp->queryKeyboardModifiers() & Qt::ShiftModifier) { + forceNonNativeDialog = true; + } +#endif + const QStringList schemes = QStringList(QStringLiteral("file")); return firstOrEmpty(getFilePaths(dialogParent(parent), caption, diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index bbeb906ee34..f46f83d4c36 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -110,7 +110,8 @@ public: static FilePath getExistingDirectory(QWidget *parent, const QString &caption, const FilePath &dir = {}, - QFileDialog::Options options = QFileDialog::ShowDirsOnly); + QFileDialog::Options options = QFileDialog::ShowDirsOnly, + bool fromDeviceIfShiftIsPressed = false); static FilePaths getOpenFilePaths(QWidget *parent, const QString &caption, From ce6ab1a14d59acdf19860a446055130a179b76f6 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 17 Oct 2022 13:18:03 +0200 Subject: [PATCH 126/143] ImageViewer: Improve tool bar size and behavior Merge the various export functionality into one tool button instead of three, for this not-so-often used functionality. Put the actions into a QToolBar, so if the editor area is made so small that not all actions fit anymore, the tool bar gets the standard extension button for accessing the "overflowing" ones. Fixes: QTCREATORBUG-28309 Change-Id: I2e1fcee9414038aca49648f18f61e1f15ecf3e5a Reviewed-by: Reviewed-by: Qt CI Bot Reviewed-by: Alessandro Portale --- .../actionmanager/commandbutton.cpp | 118 +++++++-- .../coreplugin/actionmanager/commandbutton.h | 21 +- src/plugins/imageviewer/imageviewer.cpp | 231 ++++++++---------- 3 files changed, 230 insertions(+), 140 deletions(-) diff --git a/src/plugins/coreplugin/actionmanager/commandbutton.cpp b/src/plugins/coreplugin/actionmanager/commandbutton.cpp index 1d7be701414..56866ca1dcb 100644 --- a/src/plugins/coreplugin/actionmanager/commandbutton.cpp +++ b/src/plugins/coreplugin/actionmanager/commandbutton.cpp @@ -5,34 +5,42 @@ #include "actionmanager.h" #include "command.h" - #include +#include using namespace Core; using namespace Utils; +/*! + \class Core::CommandAction + \inheaderfile coreplugin/actionmanager/commandbutton.h + \inmodule QtCreator + + \brief The CommandAction class is an action associated with one of + the registered Command objects. + + It shares the icon and text of the command. + The tooltip of the action consists of toolTipBase property value and Command's + key sequence which is automatically updated when user changes it. + */ + /*! \class Core::CommandButton \inheaderfile coreplugin/actionmanager/commandbutton.h \inmodule QtCreator - \brief The CommandButton class is a tool button associated with one of + \brief The CommandButton class is an action associated with one of the registered Command objects. - Tooltip of this button consists of toolTipBase property value and Command's + The tooltip of the button consists of toolTipBase property value and Command's key sequence which is automatically updated when user changes it. */ -/*! - \property CommandButton::toolTipBase - \brief The tool tip base for the command button. -*/ - /*! \internal */ -CommandButton::CommandButton(QWidget *parent) - : QToolButton(parent) +CommandAction::CommandAction(QWidget *parent) + : QAction(parent) , m_command(nullptr) { } @@ -40,8 +48,8 @@ CommandButton::CommandButton(QWidget *parent) /*! \internal */ -CommandButton::CommandButton(Id id, QWidget *parent) - : QToolButton(parent) +CommandAction::CommandAction(Id id, QWidget *parent) + : QAction(parent) , m_command(nullptr) { setCommandId(id); @@ -50,12 +58,83 @@ CommandButton::CommandButton(Id id, QWidget *parent) /*! Sets the ID of the command associated with this tool button to \a id. */ -void CommandButton::setCommandId(Id id) +void CommandAction::setCommandId(Id id) { if (m_command) - disconnect(m_command.data(), &Command::keySequenceChanged, this, &CommandButton::updateToolTip); + disconnect(m_command.data(), + &Command::keySequenceChanged, + this, + &CommandAction::updateToolTip); m_command = ActionManager::command(id); + QTC_ASSERT(m_command, return); + + if (m_toolTipBase.isEmpty()) + m_toolTipBase = m_command->description(); + + setIcon(m_command->action()->icon()); + setIconText(m_command->action()->iconText()); + setText(m_command->action()->text()); + + updateToolTip(); + connect(m_command.data(), &Command::keySequenceChanged, this, &CommandAction::updateToolTip); +} + +/*! + The base tool tip that is extended with the command's shortcut. + Defaults to the command's description. + + \sa Command::description() +*/ +QString CommandAction::toolTipBase() const +{ + return m_toolTipBase; +} + +/*! + Sets the base tool tip that is extended with the command's shortcut. + + \sa toolTipBase() +*/ +void CommandAction::setToolTipBase(const QString &toolTipBase) +{ + m_toolTipBase = toolTipBase; + updateToolTip(); +} + +void CommandAction::updateToolTip() +{ + if (m_command) + setToolTip(Utils::ProxyAction::stringWithAppendedShortcut(m_toolTipBase, + m_command->keySequence())); +} + +/*! + \internal +*/ +CommandButton::CommandButton(QWidget *parent) + : QToolButton(parent) +{} + +/*! + \internal +*/ +CommandButton::CommandButton(Utils::Id id, QWidget *parent) + : QToolButton(parent) +{ + setCommandId(id); +} + +void CommandButton::setCommandId(Utils::Id id) +{ + if (m_command) + disconnect(m_command.data(), + &Command::keySequenceChanged, + this, + &CommandButton::updateToolTip); + + m_command = ActionManager::command(id); + QTC_ASSERT(m_command, return); if (m_toolTipBase.isEmpty()) m_toolTipBase = m_command->description(); @@ -64,11 +143,22 @@ void CommandButton::setCommandId(Id id) connect(m_command.data(), &Command::keySequenceChanged, this, &CommandButton::updateToolTip); } +/*! + The base tool tip that is extended with the command's shortcut. + Defaults to the command's description. + + \sa Command::description() +*/ QString CommandButton::toolTipBase() const { return m_toolTipBase; } +/*! + Sets the base tool tip that is extended with the command's shortcut. + + \sa toolTipBase() +*/ void CommandButton::setToolTipBase(const QString &toolTipBase) { m_toolTipBase = toolTipBase; diff --git a/src/plugins/coreplugin/actionmanager/commandbutton.h b/src/plugins/coreplugin/actionmanager/commandbutton.h index af99ca4ff1a..31150209765 100644 --- a/src/plugins/coreplugin/actionmanager/commandbutton.h +++ b/src/plugins/coreplugin/actionmanager/commandbutton.h @@ -15,10 +15,28 @@ namespace Core { class Command; +class CORE_EXPORT CommandAction : public QAction +{ + Q_OBJECT + +public: + explicit CommandAction(QWidget *parent = nullptr); + explicit CommandAction(Utils::Id id, QWidget *parent = nullptr); + void setCommandId(Utils::Id id); + QString toolTipBase() const; + void setToolTipBase(const QString &toolTipBase); + +private: + void updateToolTip(); + + QPointer m_command; + QString m_toolTipBase; +}; + class CORE_EXPORT CommandButton : public QToolButton { Q_OBJECT - Q_PROPERTY(QString toolTipBase READ toolTipBase WRITE setToolTipBase) + public: explicit CommandButton(QWidget *parent = nullptr); explicit CommandButton(Utils::Id id, QWidget *parent = nullptr); @@ -32,5 +50,4 @@ private: QPointer m_command; QString m_toolTipBase; }; - } diff --git a/src/plugins/imageviewer/imageviewer.cpp b/src/plugins/imageviewer/imageviewer.cpp index a448f2fe478..be6f620bf31 100644 --- a/src/plugins/imageviewer/imageviewer.cpp +++ b/src/plugins/imageviewer/imageviewer.cpp @@ -28,8 +28,9 @@ #include #include #include +#include #include -#include +#include #include using namespace Core; @@ -44,16 +45,17 @@ struct ImageViewerPrivate ImageView *imageView; QWidget *toolbar; - CommandButton *toolButtonExportImage; - CommandButton *toolButtonMultiExportImages; - CommandButton *toolButtonCopyDataUrl; - CommandButton *toolButtonBackground; - CommandButton *toolButtonOutline; - CommandButton *toolButtonFitToScreen; - CommandButton *toolButtonOriginalSize; - CommandButton *toolButtonZoomIn; - CommandButton *toolButtonZoomOut; - CommandButton *toolButtonPlayPause; + QToolButton *shareButton; + CommandAction *actionExportImage; + CommandAction *actionMultiExportImages; + CommandAction *actionButtonCopyDataUrl; + CommandAction *actionBackground; + CommandAction *actionOutline; + CommandAction *actionFitToScreen; + CommandAction *actionOriginalSize; + CommandAction *actionZoomIn; + CommandAction *actionZoomOut; + CommandAction *actionPlayPause; QLabel *labelImageSize; QLabel *labelInfo; }; @@ -63,12 +65,12 @@ struct ImageViewerPrivate from the current theme. Returns \c true if icon is updated, \c false otherwise. */ -static bool updateButtonIconByTheme(QAbstractButton *button, const QString &name) +static bool updateIconByTheme(QAction *action, const QString &name) { QTC_ASSERT(!name.isEmpty(), return false); if (QIcon::hasThemeIcon(name)) { - button->setIcon(QIcon::fromTheme(name)); + action->setIcon(QIcon::fromTheme(name)); return true; } @@ -100,135 +102,116 @@ void ImageViewer::ctor() setDuplicateSupported(true); // toolbar - d->toolbar = new QWidget; + d->toolbar = new StyledBar; - d->toolButtonExportImage = new CommandButton; - d->toolButtonMultiExportImages = new CommandButton; - d->toolButtonCopyDataUrl = new CommandButton; - d->toolButtonBackground = new CommandButton; - d->toolButtonOutline = new CommandButton; - d->toolButtonFitToScreen = new CommandButton; - d->toolButtonOriginalSize = new CommandButton; - d->toolButtonZoomIn = new CommandButton; - d->toolButtonZoomOut = new CommandButton; - d->toolButtonPlayPause = new CommandButton; + d->actionExportImage = new CommandAction(Constants::ACTION_EXPORT_IMAGE, d->toolbar); + d->actionMultiExportImages = new CommandAction(Constants::ACTION_EXPORT_MULTI_IMAGES, + d->toolbar); + d->actionButtonCopyDataUrl = new CommandAction(Constants::ACTION_COPY_DATA_URL, d->toolbar); + d->shareButton = new QToolButton; + d->shareButton->setToolTip(Tr::tr("Export")); + d->shareButton->setPopupMode(QToolButton::InstantPopup); + d->shareButton->setIcon(Icons::EXPORTFILE_TOOLBAR.icon()); + d->shareButton->setProperty("noArrow", true); + auto shareMenu = new QMenu(d->shareButton); + shareMenu->addAction(d->actionExportImage); + shareMenu->addAction(d->actionMultiExportImages); + shareMenu->addAction(d->actionButtonCopyDataUrl); + d->shareButton->setMenu(shareMenu); - d->toolButtonBackground->setCheckable(true); - d->toolButtonBackground->setChecked(settings.showBackground); + d->actionBackground = new CommandAction(Constants::ACTION_BACKGROUND, d->toolbar); + d->actionOutline = new CommandAction(Constants::ACTION_OUTLINE, d->toolbar); + d->actionFitToScreen = new CommandAction(Constants::ACTION_FIT_TO_SCREEN, d->toolbar); + d->actionOriginalSize = new CommandAction(Core::Constants::ZOOM_RESET, d->toolbar); + d->actionZoomIn = new CommandAction(Core::Constants::ZOOM_IN, d->toolbar); + d->actionZoomOut = new CommandAction(Core::Constants::ZOOM_OUT, d->toolbar); + d->actionPlayPause = new CommandAction(Constants::ACTION_TOGGLE_ANIMATION, d->toolbar); - d->toolButtonOutline->setCheckable(true); - d->toolButtonOutline->setChecked(settings.showOutline); + d->actionBackground->setCheckable(true); + d->actionBackground->setChecked(settings.showBackground); - d->toolButtonFitToScreen->setCheckable(true); - d->toolButtonFitToScreen->setChecked(settings.fitToScreen); + d->actionOutline->setCheckable(true); + d->actionOutline->setChecked(settings.showOutline); - d->toolButtonZoomIn->setAutoRepeat(true); + d->actionFitToScreen->setCheckable(true); + d->actionFitToScreen->setChecked(settings.fitToScreen); - d->toolButtonZoomOut->setAutoRepeat(true); + d->actionZoomIn->setAutoRepeat(true); - d->toolButtonExportImage->setToolTipBase(Tr::tr("Export as Image")); - d->toolButtonMultiExportImages->setToolTipBase(Tr::tr("Export Images of Multiple Sizes")); - d->toolButtonOutline->setToolTipBase(Tr::tr("Show Outline")); - d->toolButtonFitToScreen->setToolTipBase(Tr::tr("Fit to Screen")); - d->toolButtonOriginalSize->setToolTipBase(Tr::tr("Original Size")); - d->toolButtonZoomIn->setToolTipBase(Tr::tr("Zoom In")); - d->toolButtonZoomOut->setToolTipBase(Tr::tr("Zoom Out")); + d->actionZoomOut->setAutoRepeat(true); - d->toolButtonExportImage->setIcon(Icons::EXPORTFILE_TOOLBAR.icon()); - d->toolButtonMultiExportImages->setIcon(Icons::MULTIEXPORTFILE_TOOLBAR.icon()); - d->toolButtonCopyDataUrl->setIcon(Icons::COPY_TOOLBAR.icon()); const Icon backgroundIcon({{":/utils/images/desktopdevicesmall.png", Theme::IconsBaseColor}}); - d->toolButtonBackground->setIcon(backgroundIcon.icon()); - d->toolButtonOutline->setIcon(Icons::BOUNDING_RECT.icon()); - d->toolButtonZoomIn->setIcon( - ActionManager::command(Core::Constants::ZOOM_IN)->action()->icon()); - d->toolButtonZoomOut->setIcon( - ActionManager::command(Core::Constants::ZOOM_OUT)->action()->icon()); - d->toolButtonOriginalSize->setIcon( - ActionManager::command(Core::Constants::ZOOM_RESET)->action()->icon()); - d->toolButtonFitToScreen->setIcon(Icons::FITTOVIEW_TOOLBAR.icon()); + d->actionBackground->setIcon(backgroundIcon.icon()); + d->actionOutline->setIcon(Icons::BOUNDING_RECT.icon()); + d->actionZoomIn->setIcon(ActionManager::command(Core::Constants::ZOOM_IN)->action()->icon()); + d->actionZoomOut->setIcon(ActionManager::command(Core::Constants::ZOOM_OUT)->action()->icon()); + d->actionOriginalSize->setIcon( + ActionManager::command(Core::Constants::ZOOM_RESET)->action()->icon()); + d->actionFitToScreen->setIcon(Icons::FITTOVIEW_TOOLBAR.icon()); // icons update - try to use system theme - updateButtonIconByTheme(d->toolButtonFitToScreen, QLatin1String("zoom-fit-best")); + updateIconByTheme(d->actionFitToScreen, QLatin1String("zoom-fit-best")); // a display - something is on the background - updateButtonIconByTheme(d->toolButtonBackground, QLatin1String("video-display")); + updateIconByTheme(d->actionBackground, QLatin1String("video-display")); // "emblem to specify the directory where the user stores photographs" // (photograph has outline - piece of paper) - updateButtonIconByTheme(d->toolButtonOutline, QLatin1String("emblem-photos")); + updateIconByTheme(d->actionOutline, QLatin1String("emblem-photos")); - auto setAsDefaultButton = new QToolButton; - auto setAsDefault = new QAction(Tr::tr("Set as Default"), setAsDefaultButton); + auto setAsDefault = new QAction(Tr::tr("Set as Default"), d->toolbar); setAsDefault->setToolTip(Tr::tr("Use the current settings for background, outline, and fitting " "to screen as the default for new image viewers.")); - setAsDefaultButton->setDefaultAction(setAsDefault); - - d->toolButtonExportImage->setCommandId(Constants::ACTION_EXPORT_IMAGE); - d->toolButtonMultiExportImages->setCommandId(Constants::ACTION_EXPORT_MULTI_IMAGES); - d->toolButtonCopyDataUrl->setCommandId(Constants::ACTION_COPY_DATA_URL); - d->toolButtonZoomIn->setCommandId(Core::Constants::ZOOM_IN); - d->toolButtonZoomOut->setCommandId(Core::Constants::ZOOM_OUT); - d->toolButtonOriginalSize->setCommandId(Core::Constants::ZOOM_RESET); - d->toolButtonFitToScreen->setCommandId(Constants::ACTION_FIT_TO_SCREEN); - d->toolButtonBackground->setCommandId(Constants::ACTION_BACKGROUND); - d->toolButtonOutline->setCommandId(Constants::ACTION_OUTLINE); - d->toolButtonPlayPause->setCommandId(Constants::ACTION_TOGGLE_ANIMATION); d->labelImageSize = new QLabel; d->labelInfo = new QLabel; + auto bar = new QToolBar; + bar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + + bar->addWidget(d->shareButton); + bar->addSeparator(); + bar->addAction(d->actionOriginalSize); + bar->addAction(d->actionZoomIn); + bar->addAction(d->actionZoomOut); + bar->addAction(d->actionPlayPause); + bar->addAction(d->actionPlayPause); + bar->addSeparator(); + bar->addAction(d->actionBackground); + bar->addAction(d->actionOutline); + bar->addAction(d->actionFitToScreen); + bar->addAction(setAsDefault); + auto horizontalLayout = new QHBoxLayout(d->toolbar); horizontalLayout->setSpacing(0); horizontalLayout->setContentsMargins(0, 0, 0, 0); - horizontalLayout->addWidget(d->toolButtonExportImage); - horizontalLayout->addWidget(d->toolButtonMultiExportImages); - horizontalLayout->addWidget(d->toolButtonCopyDataUrl); - horizontalLayout->addWidget(new StyledSeparator); - horizontalLayout->addWidget(d->toolButtonBackground); - horizontalLayout->addWidget(d->toolButtonOutline); - horizontalLayout->addWidget(d->toolButtonFitToScreen); - horizontalLayout->addWidget(setAsDefaultButton); - horizontalLayout->addWidget(new StyledSeparator); - horizontalLayout->addWidget(d->toolButtonOriginalSize); - horizontalLayout->addWidget(d->toolButtonZoomIn); - horizontalLayout->addWidget(d->toolButtonZoomOut); - horizontalLayout->addWidget(d->toolButtonPlayPause); - horizontalLayout->addWidget(d->toolButtonPlayPause); - horizontalLayout->addWidget(new StyledSeparator); - horizontalLayout->addItem(new QSpacerItem(315, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); + horizontalLayout->addWidget(bar); + horizontalLayout->addItem( + new QSpacerItem(315, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); horizontalLayout->addWidget(new StyledSeparator); horizontalLayout->addWidget(d->labelImageSize); horizontalLayout->addWidget(new StyledSeparator); horizontalLayout->addWidget(d->labelInfo); // connections - connect(d->toolButtonExportImage, &QAbstractButton::clicked, - d->imageView, &ImageView::exportImage); - connect(d->toolButtonMultiExportImages, &QAbstractButton::clicked, - d->imageView, &ImageView::exportMultiImages); - connect(d->toolButtonCopyDataUrl, &QAbstractButton::clicked, - d->imageView, &ImageView::copyDataUrl); - connect(d->toolButtonZoomIn, &QAbstractButton::clicked, - d->imageView, &ImageView::zoomIn); - connect(d->toolButtonZoomOut, &QAbstractButton::clicked, - d->imageView, &ImageView::zoomOut); - connect(d->toolButtonFitToScreen, - &QAbstractButton::toggled, + connect(d->actionExportImage, &QAction::triggered, d->imageView, &ImageView::exportImage); + connect(d->actionMultiExportImages, + &QAction::triggered, d->imageView, - &ImageView::setFitToScreen); + &ImageView::exportMultiImages); + connect(d->actionButtonCopyDataUrl, &QAction::triggered, d->imageView, &ImageView::copyDataUrl); + connect(d->actionZoomIn, &QAction::triggered, d->imageView, &ImageView::zoomIn); + connect(d->actionZoomOut, &QAction::triggered, d->imageView, &ImageView::zoomOut); + connect(d->actionFitToScreen, &QAction::triggered, d->imageView, &ImageView::setFitToScreen); connect(d->imageView, &ImageView::fitToScreenChanged, - d->toolButtonFitToScreen, - &QAbstractButton::setChecked); - connect(d->toolButtonOriginalSize, - &QAbstractButton::clicked, + d->actionFitToScreen, + &QAction::setChecked); + connect(d->actionOriginalSize, + &QAction::triggered, d->imageView, &ImageView::resetToOriginalSize); - connect(d->toolButtonBackground, &QAbstractButton::toggled, - d->imageView, &ImageView::setViewBackground); - connect(d->toolButtonOutline, &QAbstractButton::toggled, - d->imageView, &ImageView::setViewOutline); - connect(d->toolButtonPlayPause, &CommandButton::clicked, - this, &ImageViewer::playToggled); + connect(d->actionBackground, &QAction::toggled, d->imageView, &ImageView::setViewBackground); + connect(d->actionOutline, &QAction::toggled, d->imageView, &ImageView::setViewOutline); + connect(d->actionPlayPause, &QAction::triggered, this, &ImageViewer::playToggled); connect(d->file.data(), &ImageViewerFile::imageSizeChanged, this, &ImageViewer::imageSizeUpdated); connect(d->file.data(), &ImageViewerFile::openFinished, @@ -280,18 +263,18 @@ IEditor *ImageViewer::duplicate() void ImageViewer::exportImage() { if (d->file->type() == ImageViewerFile::TypeSvg) - d->toolButtonExportImage->click(); + d->actionExportImage->trigger(); } void ImageViewer::exportMultiImages() { if (d->file->type() == ImageViewerFile::TypeSvg) - d->toolButtonMultiExportImages->click(); + d->actionMultiExportImages->trigger(); } void ImageViewer::copyDataUrl() { - d->toolButtonCopyDataUrl->click(); + d->actionButtonCopyDataUrl->trigger(); } void ImageViewer::imageSizeUpdated(const QSize &size) @@ -310,45 +293,45 @@ void ImageViewer::scaleFactorUpdate(qreal factor) void ImageViewer::switchViewBackground() { - d->toolButtonBackground->click(); + d->actionBackground->trigger(); } void ImageViewer::switchViewOutline() { - d->toolButtonOutline->click(); + d->actionOutline->trigger(); } void ImageViewer::zoomIn() { - d->toolButtonZoomIn->click(); + d->actionZoomIn->trigger(); } void ImageViewer::zoomOut() { - d->toolButtonZoomOut->click(); + d->actionZoomOut->trigger(); } void ImageViewer::resetToOriginalSize() { - d->toolButtonOriginalSize->click(); + d->actionOriginalSize->trigger(); } void ImageViewer::fitToScreen() { - d->toolButtonFitToScreen->click(); + d->actionFitToScreen->trigger(); } void ImageViewer::updateToolButtons() { const bool isSvg = d->file->type() == ImageViewerFile::TypeSvg; - d->toolButtonExportImage->setEnabled(isSvg); - d->toolButtonMultiExportImages->setEnabled(isSvg); + d->actionExportImage->setEnabled(isSvg); + d->actionMultiExportImages->setEnabled(isSvg); updatePauseAction(); } void ImageViewer::togglePlay() { - d->toolButtonPlayPause->click(); + d->actionPlayPause->trigger(); } void ImageViewer::playToggled() @@ -360,12 +343,12 @@ void ImageViewer::updatePauseAction() { bool isMovie = d->file->type() == ImageViewerFile::TypeMovie; if (isMovie && !d->file->isPaused()) { - d->toolButtonPlayPause->setToolTipBase(Tr::tr("Pause Animation")); - d->toolButtonPlayPause->setIcon(Icons::INTERRUPT_SMALL_TOOLBAR.icon()); + d->actionPlayPause->setToolTipBase(Tr::tr("Pause Animation")); + d->actionPlayPause->setIcon(Icons::INTERRUPT_SMALL_TOOLBAR.icon()); } else { - d->toolButtonPlayPause->setToolTipBase(Tr::tr("Play Animation")); - d->toolButtonPlayPause->setIcon(Icons::RUN_SMALL_TOOLBAR.icon()); - d->toolButtonPlayPause->setEnabled(isMovie); + d->actionPlayPause->setToolTipBase(Tr::tr("Play Animation")); + d->actionPlayPause->setIcon(Icons::RUN_SMALL_TOOLBAR.icon()); + d->actionPlayPause->setEnabled(isMovie); } } From 9498b736dad1e783744da995a3b8a464445da495 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 18 Oct 2022 09:50:34 +0200 Subject: [PATCH 127/143] Utils: Allow Os Type selection for CommandLine Adds function overloads allowing users to choose the flavor of command line argument quoting. Change-Id: I24518162c286114333a93301fedc2a3329cd0c29 Reviewed-by: hjk --- src/libs/utils/commandline.cpp | 17 +++++++++++++++++ src/libs/utils/commandline.h | 3 +++ 2 files changed, 20 insertions(+) diff --git a/src/libs/utils/commandline.cpp b/src/libs/utils/commandline.cpp index 725fbeea24b..77e33fc83bc 100644 --- a/src/libs/utils/commandline.cpp +++ b/src/libs/utils/commandline.cpp @@ -1412,6 +1412,12 @@ CommandLine::CommandLine(const FilePath &exe, const QStringList &args) addArgs(args); } +CommandLine::CommandLine(const FilePath &exe, const QStringList &args, OsType osType) + : m_executable(exe) +{ + addArgs(args, osType); +} + CommandLine::CommandLine(const FilePath &exe, const QString &args, RawType) : m_executable(exe) { @@ -1438,12 +1444,23 @@ void CommandLine::addArg(const QString &arg) ProcessArgs::addArg(&m_arguments, arg, m_executable.osType()); } +void CommandLine::addArg(const QString &arg, OsType osType) +{ + ProcessArgs::addArg(&m_arguments, arg, osType); +} + void CommandLine::addArgs(const QStringList &inArgs) { for (const QString &arg : inArgs) addArg(arg); } +void CommandLine::addArgs(const QStringList &inArgs, OsType osType) +{ + for (const QString &arg : inArgs) + addArg(arg, osType); +} + // Adds cmd's executable and arguments one by one to this commandline. // Useful for 'sudo', 'nice', etc void CommandLine::addCommandLineAsArgs(const CommandLine &cmd) diff --git a/src/libs/utils/commandline.h b/src/libs/utils/commandline.h index 99558f5d147..67e08b7fe4d 100644 --- a/src/libs/utils/commandline.h +++ b/src/libs/utils/commandline.h @@ -115,12 +115,15 @@ public: CommandLine(); explicit CommandLine(const FilePath &executable); CommandLine(const FilePath &exe, const QStringList &args); + CommandLine(const FilePath &exe, const QStringList &args, OsType osType); CommandLine(const FilePath &exe, const QString &unparsedArgs, RawType); static CommandLine fromUserInput(const QString &cmdline, MacroExpander *expander = nullptr); void addArg(const QString &arg); + void addArg(const QString &arg, OsType osType); void addArgs(const QStringList &inArgs); + void addArgs(const QStringList &inArgs, OsType osType); void addArgs(const QString &inArgs, RawType); void prependArgs(const QStringList &inArgs); From 98cc93b027716ee2bdeb26a20928e51f5576fcba Mon Sep 17 00:00:00 2001 From: Rainer Keller Date: Mon, 17 Oct 2022 10:28:20 +0200 Subject: [PATCH 128/143] McuSupport: Iterate over target packages directly The previous code iterates over all packages filtering out every package that is not in the list of the target. Instead, the list of target packages can be iterated directly. Change-Id: I03c9b86b45acca121abe6f2098d6c0e98fc94688 Reviewed-by: Daniele Bortolotti Reviewed-by: Reviewed-by: hjk --- src/plugins/mcusupport/mcusupportoptionspage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/mcusupport/mcusupportoptionspage.cpp b/src/plugins/mcusupport/mcusupportoptionspage.cpp index 4258b816d2d..fd166245a7f 100644 --- a/src/plugins/mcusupport/mcusupportoptionspage.cpp +++ b/src/plugins/mcusupport/mcusupportoptionspage.cpp @@ -254,8 +254,8 @@ void McuSupportOptionsWidget::showMcuTargetPackages() m_packagesLayout->removeRow(0); } - for (const auto &package : std::as_const(m_options.sdkRepository.packages)) { - if (!mcuTarget->packages().contains(package) || package->label().isEmpty()) + for (const auto &package : mcuTarget->packages()) { + if (package->label().isEmpty()) continue; QWidget *packageWidget = package->widget(); m_packagesLayout->addRow(package->label(), packageWidget); From d1284570d6fe0712b1cc6fe4d0471d2871889af1 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 18 Oct 2022 08:16:02 +0200 Subject: [PATCH 129/143] ProjectExplorer: Simplify node creation Simplifies node creation by not trying to manually split / concatenate paths. Change-Id: I5cb4e9391eb8aef6e6942b8b424ac6ef451d1aff Reviewed-by: hjk Reviewed-by: --- src/plugins/projectexplorer/projectnodes.cpp | 39 +++++++------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp index 1b7c0546fe7..9245f7645ff 100644 --- a/src/plugins/projectexplorer/projectnodes.cpp +++ b/src/plugins/projectexplorer/projectnodes.cpp @@ -39,37 +39,24 @@ static FolderNode *recursiveFindOrCreateFolderNode(FolderNode *folder, const FilePath &overrideBaseDir, const FolderNode::FolderNodeFactory &factory) { - Utils::FilePath path = overrideBaseDir.isEmpty() ? folder->filePath() : overrideBaseDir; + QList paths; + const Utils::FilePath basePath = overrideBaseDir.isEmpty() ? folder->filePath() + : overrideBaseDir; + Utils::FilePath path = basePath.isEmpty() ? directory : basePath.relativeChildPath(directory); - Utils::FilePath directoryWithoutPrefix; - bool isRelative = false; - - if (path.isEmpty() || path.isRootPath()) { - directoryWithoutPrefix = directory; - isRelative = false; - } else { - if (directory.isChildOf(path) || directory == path) { - isRelative = true; - directoryWithoutPrefix = directory.relativeChildPath(path); - } else { - isRelative = false; - path.clear(); - directoryWithoutPrefix = directory; - } + while (!path.isEmpty()) { + paths.append(path); + path = path.parentDir(); } - QStringList parts = directoryWithoutPrefix.path().split('/', Qt::SkipEmptyParts); - if (directory.osType() != OsTypeWindows && !isRelative && !parts.isEmpty()) - parts[0].prepend('/'); + std::reverse(std::begin(paths), std::end(paths)); - ProjectExplorer::FolderNode *parent = folder; - for (const QString &part : std::as_const(parts)) { - path = path.pathAppended(part); - // Find folder in subFolders - FolderNode *next = parent->folderNode(path); + FolderNode *parent = folder; + for (const auto ¤tPath : paths) { + FolderNode *next = parent->folderNode(currentPath); if (!next) { // No FolderNode yet, so create it - auto tmp = factory(path); - tmp->setDisplayName(part); + auto tmp = factory(currentPath); + tmp->setDisplayName(currentPath.fileName()); next = tmp.get(); parent->addNode(std::move(tmp)); } From 7b0b127e90275a47267dae3be537deb4fd44f36e Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 18 Oct 2022 11:12:35 +0200 Subject: [PATCH 130/143] Docker: Fix deviceshell fallback commandline Previously the arguments of the target CommandLine were re-quoted/escaped which is error-prone and not necessary. Change-Id: I055d159de7860c7cd9549b61104519b8145c4021 Reviewed-by: Alessandro Portale --- src/plugins/docker/dockerdevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index fdc367e671b..c067204cfdc 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -454,7 +454,7 @@ CommandLine DockerDevicePrivate::withDockerExecCmd(const CommandLine &cmd, bool args << m_container; CommandLine dcmd{m_settings->dockerBinaryPath.filePath(), args}; - dcmd.addCommandLineAsArgs(cmd); + dcmd.addCommandLineAsArgs(cmd, CommandLine::Raw); return dcmd; } From 053990c35773d3051ecea608bdd7e19bf09c2024 Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 14 Oct 2022 17:48:39 +0200 Subject: [PATCH 131/143] RemoteLinux: Remove docker related device hack in install step It's getting into the way with RL->RL setups. Change-Id: I46e8330293da1ca6dfba00e413518fa3f16ef1d8 Reviewed-by: Marcus Tillmanns Reviewed-by: hjk --- src/plugins/remotelinux/makeinstallstep.cpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/plugins/remotelinux/makeinstallstep.cpp b/src/plugins/remotelinux/makeinstallstep.cpp index bdd3abdfe7f..7fa08a14e17 100644 --- a/src/plugins/remotelinux/makeinstallstep.cpp +++ b/src/plugins/remotelinux/makeinstallstep.cpp @@ -187,32 +187,22 @@ bool MakeInstallStep::init() void MakeInstallStep::finish(bool success) { if (success) { - const bool hack = makeCommand().needsDevice(); const FilePath rootDir = installRoot().onDevice(makeCommand()); m_deploymentData = DeploymentData(); m_deploymentData.setLocalInstallRoot(rootDir); - const int startPos = rootDir.toString().length(); + const int startPos = rootDir.path().length(); const auto appFileNames = transform>(buildSystem()->applicationTargets(), [](const BuildTargetInfo &appTarget) { return appTarget.targetFilePath.fileName(); }); - auto handleFile = [this, &appFileNames, startPos, hack](const FilePath &filePath) { + auto handleFile = [this, &appFileNames, startPos](const FilePath &filePath) { const DeployableFile::Type type = appFileNames.contains(filePath.fileName()) ? DeployableFile::TypeExecutable : DeployableFile::TypeNormal; - QString targetDir = filePath.parentDir().toString().mid(startPos); - // FIXME: This is conceptually the wrong place, but currently "downstream" like - // the rsync step doesn't handle full remote paths here. - targetDir = FilePath::fromString(targetDir).path(); - - // FIXME: Hack, Part#2: If the build was indeed not local, drop the remoteness. - // As we rely on shared build directory, this "maps" to the host. - if (hack) - m_deploymentData.addFile(FilePath::fromString(filePath.path()), targetDir, type); - else - m_deploymentData.addFile(filePath, targetDir, type); + const QString targetDir = filePath.parentDir().path().mid(startPos); + m_deploymentData.addFile(filePath, targetDir, type); return true; }; rootDir.iterateDirectory(handleFile, From 93dd15855e667869b2762ecfe941443eb4c4deda Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Mon, 17 Oct 2022 21:07:21 +0200 Subject: [PATCH 132/143] ProjectExplorer: Fix JsonWizardScannerGenerator Both calls of FilePath::relativePath() were "reversed". Amends: 389b1eceb95e237fba0724ce6eef10b27c195d59 Fixes: QTCREATORBUG-28312 Change-Id: Iecff3adeee8f8ce198c12289e51487a51178759e Reviewed-by: hjk Reviewed-by: Christian Kandeler --- .../projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp index a7477d0a39e..d0bf834fea4 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardscannergenerator.cpp @@ -77,7 +77,7 @@ Core::GeneratedFiles JsonWizardScannerGenerator::fileList(Utils::MacroExpander * [](const Utils::FilePath &filePath) { return int(filePath.path().count('/')); }; int minDepth = std::numeric_limits::max(); for (auto it = result.begin(); it != result.end(); ++it) { - const Utils::FilePath relPath = projectDir.relativePath(it->filePath()); + const Utils::FilePath relPath = it->filePath().relativePath(projectDir); it->setBinary(binaryPattern.match(relPath.toString()).hasMatch()); bool found = ProjectManager::canOpenProjectForMimeType(Utils::mimeTypeForFile(relPath)); if (found) { @@ -119,7 +119,7 @@ Core::GeneratedFiles JsonWizardScannerGenerator::scan(const Utils::FilePath &dir const Utils::FilePaths entries = dir.dirEntries({{}, QDir::AllEntries | QDir::NoDotAndDotDot}, QDir::DirsLast | QDir::Name); for (const Utils::FilePath &fi : entries) { - const Utils::FilePath relativePath = base.relativePath(fi); + const Utils::FilePath relativePath = fi.relativePath(base); if (fi.isDir() && matchesSubdirectoryPattern(relativePath)) { result += scan(fi, base); } else { From 0d2e237d65657dcae110f021b0d10b17d806f752 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 18 Oct 2022 10:42:41 +0200 Subject: [PATCH 133/143] Update qbs submodule to HEAD of 1.24 branch Change-Id: I7781942c78d940f35a02fc35ae86435fe6fd22be Reviewed-by: Reviewed-by: Qt CI Bot Reviewed-by: Christian Stenger --- src/shared/qbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/qbs b/src/shared/qbs index 09ff740b4e0..f200b701f73 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit 09ff740b4e0520374e84415cfc97561809152270 +Subproject commit f200b701f7307a2935f37d39db8e192d98ff7047 From 50979f16062c978e643d58a23d8bf2a9281e3e6e Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 18 Oct 2022 08:13:12 +0200 Subject: [PATCH 134/143] Qbs: Better error messages Provides more concrete error messages if the executable is not set or not executable. Change-Id: I336e9cd10ed6588d77add24af1fd0a9fc9766134 Reviewed-by: Christian Kandeler --- src/plugins/qbsprojectmanager/qbssession.cpp | 17 ++++++++++++++--- src/plugins/qbsprojectmanager/qbssession.h | 9 ++++++++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/plugins/qbsprojectmanager/qbssession.cpp b/src/plugins/qbsprojectmanager/qbssession.cpp index b083cc5cf3d..cf5c86dc1c1 100644 --- a/src/plugins/qbsprojectmanager/qbssession.cpp +++ b/src/plugins/qbsprojectmanager/qbssession.cpp @@ -187,8 +187,12 @@ void QbsSession::initialize() connect(d->packetReader, &PacketReader::packetReceived, this, &QbsSession::handlePacket); d->state = State::Initializing; const FilePath qbsExe = QbsSettings::qbsExecutableFilePath(); - if (qbsExe.isEmpty() || !qbsExe.exists()) { - QTimer::singleShot(0, this, [this] { setError(Error::QbsFailedToStart); }); + if (qbsExe.isEmpty()) { + QTimer::singleShot(0, this, [this] { setError(Error::NoQbsPath); }); + return; + } + if (!qbsExe.isExecutableFile()) { + QTimer::singleShot(0, this, [this] { setError(Error::InvalidQbsExecutable); }); return; } d->qbsProcess->setCommand({qbsExe, {"session"}}); @@ -223,6 +227,12 @@ std::optional QbsSession::lastError() const QString QbsSession::errorString(QbsSession::Error error) { switch (error) { + case Error::NoQbsPath: + return Tr::tr("No qbs executable was found, please set the path in the settings."); + case Error::InvalidQbsExecutable: + return Tr::tr("The qbs executable was not found at the specified path, or it is not " + "executable (\"%1\").") + .arg(QbsSettings::qbsExecutableFilePath().toUserOutput()); case Error::QbsQuit: return Tr::tr("The qbs process quit unexpectedly."); case Error::QbsFailedToStart: @@ -231,7 +241,8 @@ QString QbsSession::errorString(QbsSession::Error error) return Tr::tr("The qbs process sent unexpected data."); case Error::VersionMismatch: return Tr::tr("The qbs API level is not compatible with " - "what %1 expects.").arg(Core::Constants::IDE_DISPLAY_NAME); + "what %1 expects.") + .arg(Core::Constants::IDE_DISPLAY_NAME); } return QString(); // For dumb compilers. } diff --git a/src/plugins/qbsprojectmanager/qbssession.h b/src/plugins/qbsprojectmanager/qbssession.h index 8ad9972d6d8..0452bfdd575 100644 --- a/src/plugins/qbsprojectmanager/qbssession.h +++ b/src/plugins/qbsprojectmanager/qbssession.h @@ -100,7 +100,14 @@ public: ~QbsSession() override; enum class State { Initializing, Active, Inactive }; - enum class Error { QbsFailedToStart, QbsQuit, ProtocolError, VersionMismatch }; + enum class Error { + NoQbsPath, + InvalidQbsExecutable, + QbsFailedToStart, + QbsQuit, + ProtocolError, + VersionMismatch + }; std::optional lastError() const; static QString errorString(Error error); From e9ed3d6d0550d7275754b21581321bb80ac2a5a8 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 18 Oct 2022 09:31:47 +0200 Subject: [PATCH 135/143] RemoteLinux: Allow adding Qt to LD_LIBRARY_PATH When running on the remote build device. And make it work with CMake. Change-Id: If25bef8ab836c1d59a586116b3c3447a29c4e7e8 Reviewed-by: Christian Kandeler --- .../fileapidataextractor.cpp | 9 ++++----- .../remotelinux/remotelinuxrunconfiguration.cpp | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp index 4abee299646..da791d51642 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp @@ -264,18 +264,18 @@ QList generateBuildTargets(const PreprocessedData &input, continue; const FilePath buildDir = haveLibrariesRelativeToBuildDirectory ? buildDirectory : currentBuildDir; - FilePath tmp = buildDir.resolvePath(FilePath::fromUserInput(part)); + FilePath tmp = buildDir.resolvePath(FilePath::fromUserInput(part).onDevice(buildDir)); if (f.role == "libraries") tmp = tmp.parentDir(); if (!tmp.isEmpty() && tmp.isDir()) { // f.role is libraryPath or frameworkPath - // On Linux, exclude sub-paths from "/lib(64)", "/usr/lib(64)" and + // On *nix, exclude sub-paths from "/lib(64)", "/usr/lib(64)" and // "/usr/local/lib" since these are usually in the standard search // paths. There probably are more, but the naming schemes are arbitrary // so we'd need to ask the linker ("ld --verbose | grep SEARCH_DIR"). - if (!HostOsInfo::isLinuxHost() + if (buildDir.osType() == OsTypeWindows || !isChildOf(tmp, {"/lib", "/lib64", @@ -285,9 +285,8 @@ QList generateBuildTargets(const PreprocessedData &input, librarySeachPaths.append(tmp); // Libraries often have their import libs in ../lib and the // actual dll files in ../bin on windows. Qt is one example of that. - if (tmp.fileName() == "lib" && HostOsInfo::isWindowsHost()) { + if (tmp.fileName() == "lib" && buildDir.osType() == OsTypeWindows) { const FilePath path = tmp.parentDir().pathAppended("bin"); - if (path.isDir()) librarySeachPaths.append(path); } diff --git a/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp b/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp index 106b8b25871..99ff7d7ff2c 100644 --- a/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp +++ b/src/plugins/remotelinux/remotelinuxrunconfiguration.cpp @@ -53,13 +53,22 @@ RemoteLinuxRunConfiguration::RemoteLinuxRunConfiguration(Target *target, Id id) if (HostOsInfo::isAnyUnixHost()) addAspect(macroExpander()); - setUpdater([this, target, exeAspect, symbolsAspect] { + auto libAspect = addAspect(); + libAspect->setValue(false); + connect(libAspect, &UseLibraryPathsAspect::changed, + envAspect, &EnvironmentAspect::environmentChanged); + + setUpdater([this, target, exeAspect, symbolsAspect, libAspect] { BuildTargetInfo bti = buildTargetInfo(); const FilePath localExecutable = bti.targetFilePath; DeployableFile depFile = target->deploymentData().deployableForLocalFile(localExecutable); exeAspect->setExecutable(FilePath::fromString(depFile.remoteFilePath())); symbolsAspect->setFilePath(localExecutable); + + const IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(target->kit()); + const IDeviceConstPtr runDevice = DeviceKitAspect::device(target->kit()); + libAspect->setEnabled(buildDevice == runDevice); }); setRunnableModifier([this](Runnable &r) { @@ -67,6 +76,12 @@ RemoteLinuxRunConfiguration::RemoteLinuxRunConfiguration(Target *target, Id id) r.extraData.insert("Ssh.X11ForwardToDisplay", forwardingAspect->display()); }); + envAspect->addModifier([this, libAspect](Environment &env) { + BuildTargetInfo bti = buildTargetInfo(); + if (bti.runEnvModifier) + bti.runEnvModifier(env, libAspect->value()); + }); + connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); connect(target, &Target::deploymentDataChanged, this, &RunConfiguration::update); connect(target, &Target::kitChanged, this, &RunConfiguration::update); From 508dd23de46ab0d101bbf58c875c2b3ad8c37824 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 18 Oct 2022 09:55:58 +0200 Subject: [PATCH 136/143] Utils: Use Linux quoting in UnixDeviceFileAccess This forces all commands in UnixDeviceFileAccess to use Unix style quoting of commands. Change-Id: Ia8a2691b7058789a7afa95a9bb87f155a36cf680 Reviewed-by: David Schulz --- src/libs/utils/devicefileaccess.cpp | 54 ++++++++++++++--------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp index 956b524e724..6123cd6751c 100644 --- a/src/libs/utils/devicefileaccess.cpp +++ b/src/libs/utils/devicefileaccess.cpp @@ -629,72 +629,72 @@ bool UnixDeviceFileAccess::runInShellSuccess(const CommandLine &cmdLine, bool UnixDeviceFileAccess::isExecutableFile(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess({"test", {"-x", path}}); + return runInShellSuccess({"test", {"-x", path}, OsType::OsTypeLinux}); } bool UnixDeviceFileAccess::isReadableFile(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess({"test", {"-r", path, "-a", "-f", path}}); + return runInShellSuccess({"test", {"-r", path, "-a", "-f", path}, OsType::OsTypeLinux}); } bool UnixDeviceFileAccess::isWritableFile(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess({"test", {"-w", path, "-a", "-f", path}}); + return runInShellSuccess({"test", {"-w", path, "-a", "-f", path}, OsType::OsTypeLinux}); } bool UnixDeviceFileAccess::isReadableDirectory(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess({"test", {"-r", path, "-a", "-d", path}}); + return runInShellSuccess({"test", {"-r", path, "-a", "-d", path}, OsType::OsTypeLinux}); } bool UnixDeviceFileAccess::isWritableDirectory(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess({"test", {"-w", path, "-a", "-d", path}}); + return runInShellSuccess({"test", {"-w", path, "-a", "-d", path}, OsType::OsTypeLinux}); } bool UnixDeviceFileAccess::isFile(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess({"test", {"-f", path}}); + return runInShellSuccess({"test", {"-f", path}, OsType::OsTypeLinux}); } bool UnixDeviceFileAccess::isDirectory(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess({"test", {"-d", path}}); + return runInShellSuccess({"test", {"-d", path}, OsType::OsTypeLinux}); } bool UnixDeviceFileAccess::isSymLink(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess({"test", {"-h", path}}); + return runInShellSuccess({"test", {"-h", path}, OsType::OsTypeLinux}); } bool UnixDeviceFileAccess::ensureExistingFile(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess({"touch", {path}}); + return runInShellSuccess({"touch", {path}, OsType::OsTypeLinux}); } bool UnixDeviceFileAccess::createDirectory(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess({"mkdir", {"-p", path}}); + return runInShellSuccess({"mkdir", {"-p", path}, OsType::OsTypeLinux}); } bool UnixDeviceFileAccess::exists(const FilePath &filePath) const { const QString path = filePath.path(); - return runInShellSuccess({"test", {"-e", path}}); + return runInShellSuccess({"test", {"-e", path}, OsType::OsTypeLinux}); } bool UnixDeviceFileAccess::removeFile(const FilePath &filePath) const { - return runInShellSuccess({"rm", {filePath.path()}}); + return runInShellSuccess({"rm", {filePath.path()}, OsType::OsTypeLinux}); } bool UnixDeviceFileAccess::removeRecursively(const FilePath &filePath, QString *error) const @@ -710,7 +710,7 @@ bool UnixDeviceFileAccess::removeRecursively(const FilePath &filePath, QString * levelsNeeded = 2; QTC_ASSERT(path.count('/') >= levelsNeeded, return false); - RunResult result = runInShell({"rm", {"-rf", "--", path}}); + RunResult result = runInShell({"rm", {"-rf", "--", path}, OsType::OsTypeLinux}); if (error) *error = QString::fromUtf8(result.stdErr); return result.exitCode == 0; @@ -718,17 +718,17 @@ bool UnixDeviceFileAccess::removeRecursively(const FilePath &filePath, QString * bool UnixDeviceFileAccess::copyFile(const FilePath &filePath, const FilePath &target) const { - return runInShellSuccess({"cp", {filePath.path(), target.path()}}); + return runInShellSuccess({"cp", {filePath.path(), target.path()}, OsType::OsTypeLinux}); } bool UnixDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const { - return runInShellSuccess({"mv", {filePath.path(), target.path()}}); + return runInShellSuccess({"mv", {filePath.path(), target.path()}, OsType::OsTypeLinux}); } FilePath UnixDeviceFileAccess::symLinkTarget(const FilePath &filePath) const { - const RunResult result = runInShell({"readlink", {"-n", "-e", filePath.path()}}); + const RunResult result = runInShell({"readlink", {"-n", "-e", filePath.path()}, OsType::OsTypeLinux}); const QString out = QString::fromUtf8(result.stdOut); return out.isEmpty() ? FilePath() : filePath.withNewPath(out); } @@ -746,7 +746,7 @@ std::optional UnixDeviceFileAccess::fileContents( args += QString("seek=%1").arg(offset / gcd); } - const RunResult r = runInShell({"dd", args}); + const RunResult r = runInShell({"dd", args, OsType::OsTypeLinux}); if (r.exitCode != 0) return {}; @@ -764,7 +764,7 @@ bool UnixDeviceFileAccess::writeFileContents( args.append("bs=1"); args.append(QString("seek=%1").arg(offset)); } - return runInShellSuccess({"dd", args}, data); + return runInShellSuccess({"dd", args, OsType::OsTypeLinux}, data); } OsType UnixDeviceFileAccess::osType(const FilePath &filePath) const @@ -775,7 +775,7 @@ OsType UnixDeviceFileAccess::osType(const FilePath &filePath) const QDateTime UnixDeviceFileAccess::lastModified(const FilePath &filePath) const { - const RunResult result = runInShell({"stat", {"-L", "-c", "%Y", filePath.path()}}); + const RunResult result = runInShell({"stat", {"-L", "-c", "%Y", filePath.path()}, OsType::OsTypeLinux}); qint64 secs = result.stdOut.toLongLong(); const QDateTime dt = QDateTime::fromSecsSinceEpoch(secs, Qt::UTC); return dt; @@ -783,7 +783,7 @@ QDateTime UnixDeviceFileAccess::lastModified(const FilePath &filePath) const QFile::Permissions UnixDeviceFileAccess::permissions(const FilePath &filePath) const { - const RunResult result = runInShell({"stat", {"-L", "-c", "%a", filePath.path()}}); + const RunResult result = runInShell({"stat", {"-L", "-c", "%a", filePath.path()}, OsType::OsTypeLinux}); const uint bits = result.stdOut.toUInt(nullptr, 8); QFileDevice::Permissions perm = {}; #define BIT(n, p) if (bits & (1< Date: Fri, 14 Oct 2022 10:06:21 +0200 Subject: [PATCH 137/143] RemoteLinux: Fix deployment when building remotely By using a generic file transfer method based of FilePath::copyFile() that doesn't require either of the transfer end point to be the local host. Change-Id: Ia2e4273df52f5ce6533046d96be3f6b521b7f0a5 Reviewed-by: Christian Kandeler --- .../devicesupport/filetransfer.cpp | 16 +++-- .../devicesupport/filetransferinterface.h | 1 + src/plugins/remotelinux/linuxdevice.cpp | 61 +++++++++++++++++++ src/plugins/remotelinux/linuxdevicetester.cpp | 1 + .../remotelinuxdeployconfiguration.cpp | 10 ++- 5 files changed, 82 insertions(+), 7 deletions(-) diff --git a/src/plugins/projectexplorer/devicesupport/filetransfer.cpp b/src/plugins/projectexplorer/devicesupport/filetransfer.cpp index 54a135faf9e..bfbfdcf824c 100644 --- a/src/plugins/projectexplorer/devicesupport/filetransfer.cpp +++ b/src/plugins/projectexplorer/devicesupport/filetransfer.cpp @@ -98,12 +98,17 @@ void FileTransferPrivate::start() return startFailed(tr("No files to transfer.")); const FileTransferDirection direction = transferDirection(m_setup.m_files); - if (direction == FileTransferDirection::Invalid) - return startFailed(tr("Mixing different types of transfer in one go.")); - const IDeviceConstPtr device = matchedDevice(direction, m_setup.m_files); - if (!device) - return startFailed(tr("Trying to transfer into / from not matching device.")); + IDeviceConstPtr device; + if (direction != FileTransferDirection::Invalid) + device = matchedDevice(direction, m_setup.m_files); + + if (!device) { + // Fall back to generic copy. + const FilePath &filePath = m_setup.m_files.first().m_target; + device = DeviceManager::deviceForPath(filePath); + m_setup.m_method = FileTransferMethod::GenericCopy; + } run(m_setup, device); } @@ -190,6 +195,7 @@ QString FileTransfer::transferMethodName(FileTransferMethod method) switch (method) { case FileTransferMethod::Sftp: return FileTransfer::tr("sftp"); case FileTransferMethod::Rsync: return FileTransfer::tr("rsync"); + case FileTransferMethod::GenericCopy: return FileTransfer::tr("generic file copy"); } QTC_CHECK(false); return {}; diff --git a/src/plugins/projectexplorer/devicesupport/filetransferinterface.h b/src/plugins/projectexplorer/devicesupport/filetransferinterface.h index ad4ccc200ef..543e1ce6c29 100644 --- a/src/plugins/projectexplorer/devicesupport/filetransferinterface.h +++ b/src/plugins/projectexplorer/devicesupport/filetransferinterface.h @@ -20,6 +20,7 @@ enum class FileTransferDirection { enum class FileTransferMethod { Sftp, Rsync, + GenericCopy, Default = Sftp }; diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 5f820126d95..6aee5591945 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -1428,12 +1428,73 @@ private: int m_currentIndex = 0; }; +class GenericTransferImpl : public FileTransferInterface +{ +public: + GenericTransferImpl(const FileTransferSetupData &setup, LinuxDevicePrivate *) + : FileTransferInterface(setup) + {} + +private: + void start() final + { + m_fileCount = m_setup.m_files.size(); + m_currentIndex = 0; + m_checkedDirectories.clear(); + nextFile(); + } + + void nextFile() + { + ProcessResultData result; + if (m_currentIndex >= m_fileCount) { + emit done(result); + return; + } + + const FileToTransfer &file = m_setup.m_files.at(m_currentIndex); + const FilePath &source = file.m_source; + const FilePath &target = file.m_target; + ++m_currentIndex; + + const FilePath targetDir = target.parentDir(); + if (!m_checkedDirectories.contains(targetDir)) { + emit progress(tr("Creating directory: %1") + .arg(targetDir.toUserOutput())); + if (!targetDir.ensureWritableDir()) { + result.m_errorString = tr("Failed."); + result.m_exitCode = -1; // Random pick + emit done(result); + return; + } + m_checkedDirectories.insert(targetDir); + } + + emit progress(tr("Copying %1/%2: %3 -> %4") + .arg(m_currentIndex).arg(m_fileCount).arg(source.toUserOutput(), target.toUserOutput())); + if (!source.copyFile(target)) { + result.m_errorString = tr("Failed."); + result.m_exitCode = -1; // Random pick + emit done(result); + return; + } + + // FIXME: Use asyncCopyFile instead + nextFile(); + } + + int m_currentIndex = 0; + int m_fileCount = 0; + QSet m_checkedDirectories; +}; + 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); } QTC_CHECK(false); return {}; diff --git a/src/plugins/remotelinux/linuxdevicetester.cpp b/src/plugins/remotelinux/linuxdevicetester.cpp index 32875a54e28..e03b322bff3 100644 --- a/src/plugins/remotelinux/linuxdevicetester.cpp +++ b/src/plugins/remotelinux/linuxdevicetester.cpp @@ -236,6 +236,7 @@ void GenericLinuxDeviceTester::testFileTransfer(FileTransferMethod method) switch (method) { case FileTransferMethod::Sftp: d->state = TestingSftp; break; case FileTransferMethod::Rsync: d->state = TestingRsync; break; + case FileTransferMethod::GenericCopy: QTC_CHECK(false) /* not tested */; break; } emit progressMessage(Tr::tr("Checking whether \"%1\" works...") .arg(FileTransfer::transferMethodName(method))); diff --git a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp index 721107afebb..862925deb22 100644 --- a/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp +++ b/src/plugins/remotelinux/remotelinuxdeployconfiguration.cpp @@ -39,6 +39,9 @@ RemoteLinuxDeployConfigurationFactory::RemoteLinuxDeployConfigurationFactory() addInitialStep(Constants::MakeInstallStepId, needsMakeInstall); addInitialStep(Constants::KillAppStepId); + + // Todo: Check: Instead of having two 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()); @@ -49,8 +52,11 @@ RemoteLinuxDeployConfigurationFactory::RemoteLinuxDeployConfigurationFactory() return runDevice && runDevice->extraData(Constants::SupportsRSync).toBool(); }); addInitialStep(Constants::DirectUploadStepId, [](Target *target) { - auto device = DeviceKitAspect::device(target->kit()); - return device && !device->extraData(Constants::SupportsRSync).toBool(); + auto runDevice = DeviceKitAspect::device(target->kit()); + auto buildDevice = BuildDeviceKitAspect::device(target->kit()); + if (runDevice == buildDevice) + return true; + return runDevice && !runDevice->extraData(Constants::SupportsRSync).toBool(); }); } From 4ebfd4b6cf85617f88211336378312582c106513 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Fri, 14 Oct 2022 09:46:15 +0200 Subject: [PATCH 138/143] Doc: Update instructions for adding B2Qt Devices Update screenshots to use the dark mode. Task-number: QTCREATORBUG-27876 Change-Id: I1b6483f76d7f14485ffc13b465e84f378494ea3f Reviewed-by: Samuli Piippo --- ...tcreator-boot2qt-device-configurations.png | Bin 13529 -> 21668 bytes .../images/qtcreator-devices-boot2qt.png | Bin 4897 -> 8391 bytes doc/qtcreator/src/linux-mobile/b2qtdev.qdoc | 5 +++-- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/qtcreator/images/qtcreator-boot2qt-device-configurations.png b/doc/qtcreator/images/qtcreator-boot2qt-device-configurations.png index cddc2ea4d2f6d6f8e41c640d6a7719def22280c5..584c595dcb333f96fb905b9b2c8db4e7fc6a76ef 100644 GIT binary patch literal 21668 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 z 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 literal 4897 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 \uicontrol Preferences > \uicontrol Devices > \uicontrol Devices > \uicontrol Add > - \uicontrol Boot2Qt > \uicontrol {Finish}. + \uicontrol Boot2Qt. \image qtcreator-devices-boot2qt.png "Boot2Qt Network Device Setup wizard" \li In the \uicontrol {Device name} field, enter a name for the connection. \li In the \uicontrol {Device address} field, enter the host name or IP address of the device. This value will be available in the \c %{Device:HostAddress} variable. - \li Click \uicontrol {Next} to create the connection. + \li Click > \uicontrol {Finish} to test the connection and + add the device. You can edit the connection parameters in the \uicontrol Devices tab. The wizard does not show From 60a8e987b58da3127f82a21c2d8d51552a21c77c Mon Sep 17 00:00:00 2001 From: David Schulz Date: Tue, 18 Oct 2022 14:24:48 +0200 Subject: [PATCH 139/143] Docker: use docker device os type for file access Change-Id: I3e3e91491c77d0146f2d52777ba1d05d2f45a9d3 Reviewed-by: hjk --- src/plugins/docker/dockerdevice.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/plugins/docker/dockerdevice.cpp b/src/plugins/docker/dockerdevice.cpp index c067204cfdc..8561764688c 100644 --- a/src/plugins/docker/dockerdevice.cpp +++ b/src/plugins/docker/dockerdevice.cpp @@ -124,6 +124,7 @@ public: RunResult runInShell(const CommandLine &cmdLine, const QByteArray &stdInData) const override; QString mapToDevicePath(const FilePath &filePath) const override; + OsType osType(const FilePath &filePath) const override; DockerDevicePrivate *m_dev = nullptr; }; @@ -367,6 +368,12 @@ QString DockerDeviceFileAccess::mapToDevicePath(const FilePath &filePath) const return path; } +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)) { From f27444a2134216d49cab1e9cdb9296a9fb19e5cd Mon Sep 17 00:00:00 2001 From: Robert Loehning Date: Tue, 18 Oct 2022 13:14:08 +0200 Subject: [PATCH 140/143] Doc: Rephrase code snippet description ...to also cover the case of writing new bug reports. Change-Id: Ia98231c836df256313f7b7cb42f3e05bf3b8866b Reviewed-by: Leena Miettinen --- .../src/projects/creator-only/creator-projects-creating.qdoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 8b2f050adc8..418b4083b6f 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-creating.qdoc @@ -167,8 +167,8 @@ not use any default classes. \row \li Code Snippet - \li Creates a qmake project from a code snippet. When fixing bug - reports that contain a code snippet, you can place the code + \li Creates a qmake project from a code snippet. When working on + bug reports that contain a code snippet, you can place the code snippet into a project to compile and check it. \row \li {1,4} Non-Qt Project From 1478a3653086e44a0a625c0a4b8ab09a7d6eafa9 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Tue, 18 Oct 2022 14:18:45 +0300 Subject: [PATCH 141/143] ProjectExplorer: Fix prepending executable path for custom toolchain Amends c60c642fe5f7c9836a51845504089da2825311d6. Change-Id: I214c3d83ef5f1642d581e84f9219e929d765c471 Reviewed-by: Christian Kandeler --- .../projectexplorer/customtoolchain.cpp | 18 +++++++++--------- src/plugins/projectexplorer/customtoolchain.h | 1 - 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/plugins/projectexplorer/customtoolchain.cpp b/src/plugins/projectexplorer/customtoolchain.cpp index 2d311b43604..4c2904d3cd6 100644 --- a/src/plugins/projectexplorer/customtoolchain.cpp +++ b/src/plugins/projectexplorer/customtoolchain.cpp @@ -131,13 +131,14 @@ ToolChain::BuiltInHeaderPathsRunner CustomToolChain::createBuiltInHeaderPathsRun void CustomToolChain::addToEnvironment(Environment &env) const { - if (!m_compilerCommand.isEmpty()) { - const FilePath path = m_compilerCommand.parentDir(); - env.prependOrSetPath(path); - const FilePath makePath = m_makeCommand.parentDir(); - if (makePath != path) - env.prependOrSetPath(makePath); - } + const FilePath compiler = compilerCommand(); + if (compiler.isEmpty()) + return; + const FilePath path = compiler.parentDir(); + env.prependOrSetPath(path); + const FilePath makePath = m_makeCommand.parentDir(); + if (makePath != path) + env.prependOrSetPath(makePath); } QStringList CustomToolChain::suggestedMkspecList() const @@ -252,8 +253,7 @@ bool CustomToolChain::operator ==(const ToolChain &other) const return false; auto customTc = static_cast(&other); - return m_compilerCommand == customTc->m_compilerCommand - && m_makeCommand == customTc->m_makeCommand + return m_makeCommand == customTc->m_makeCommand && targetAbi() == customTc->targetAbi() && m_predefinedMacros == customTc->m_predefinedMacros && m_builtInHeaderPaths == customTc->m_builtInHeaderPaths; diff --git a/src/plugins/projectexplorer/customtoolchain.h b/src/plugins/projectexplorer/customtoolchain.h index d05e4c9998b..38a529d1b36 100644 --- a/src/plugins/projectexplorer/customtoolchain.h +++ b/src/plugins/projectexplorer/customtoolchain.h @@ -83,7 +83,6 @@ private: CustomParserSettings customParserSettings() const; - Utils::FilePath m_compilerCommand; Utils::FilePath m_makeCommand; Macros m_predefinedMacros; From 101ef19d1e9bec80e5475885bf74552e49eb6080 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 18 Oct 2022 11:54:39 +0200 Subject: [PATCH 142/143] ImageViewer: Show current default settings in tooltip List the current default settings in the tooltip for the "Set as Default" button. Fixes: QTCREATORBUG-28322 Change-Id: Ic3a21218a60821e328d6db2375a0e5a08616d686 Reviewed-by: Leena Miettinen Reviewed-by: Reviewed-by: Alessandro Portale --- src/plugins/imageviewer/imageviewer.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/plugins/imageviewer/imageviewer.cpp b/src/plugins/imageviewer/imageviewer.cpp index be6f620bf31..441d33dc6ba 100644 --- a/src/plugins/imageviewer/imageviewer.cpp +++ b/src/plugins/imageviewer/imageviewer.cpp @@ -158,8 +158,19 @@ void ImageViewer::ctor() updateIconByTheme(d->actionOutline, QLatin1String("emblem-photos")); auto setAsDefault = new QAction(Tr::tr("Set as Default"), d->toolbar); - setAsDefault->setToolTip(Tr::tr("Use the current settings for background, outline, and fitting " - "to screen as the default for new image viewers.")); + const auto updateSetAsDefaultToolTip = [this, setAsDefault] { + const ImageView::Settings settings = d->imageView->settings(); + const QString on = Tr::tr("on"); + const QString off = Tr::tr("off"); + setAsDefault->setToolTip( + "

" + + Tr::tr("Use the current settings for background, outline, and fitting " + "to screen as the default for new image viewers. Current default:") + + "

  • " + Tr::tr("Background: %1").arg(settings.showBackground ? on : off) + + "
  • " + Tr::tr("Outline: %1").arg(settings.showOutline ? on : off) + "
  • " + + Tr::tr("Fit to Screen: %1").arg(settings.fitToScreen ? on : off) + "
"); + }; + updateSetAsDefaultToolTip(); d->labelImageSize = new QLabel; d->labelInfo = new QLabel; @@ -226,8 +237,9 @@ void ImageViewer::ctor() this, &ImageViewer::updatePauseAction); connect(d->imageView, &ImageView::scaleFactorChanged, this, &ImageViewer::scaleFactorUpdate); - connect(setAsDefault, &QAction::triggered, d->imageView, [this] { + connect(setAsDefault, &QAction::triggered, d->imageView, [this, updateSetAsDefaultToolTip] { d->imageView->writeSettings(ICore::settings()); + updateSetAsDefaultToolTip(); }); } From 471b03680329f639927f642cef07f964014fcd24 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 18 Oct 2022 15:09:14 +0200 Subject: [PATCH 143/143] CommandButton: Fix build with older Qt versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure QAction is included not only accidentally via other Qt headers. Change-Id: I946bcdc852989f50d1464393013169c00aa8172f Reviewed-by: hjk Reviewed-by: Sivert Krøvel --- src/plugins/coreplugin/actionmanager/commandbutton.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/coreplugin/actionmanager/commandbutton.h b/src/plugins/coreplugin/actionmanager/commandbutton.h index 31150209765..72fb6231331 100644 --- a/src/plugins/coreplugin/actionmanager/commandbutton.h +++ b/src/plugins/coreplugin/actionmanager/commandbutton.h @@ -7,6 +7,7 @@ #include +#include #include #include #include