Merge remote-tracking branch 'origin/5.0'

Change-Id: I074571dac56b26a8a1449c29aef53b9052d8e304
This commit is contained in:
hjk
2021-07-22 14:53:39 +02:00
31 changed files with 2335 additions and 1767 deletions

View File

@@ -167,6 +167,26 @@ void registerNodeInstanceMetaObject(QObject *object, QQmlEngine *engine)
QQuickDesignerSupportProperties::registerNodeInstanceMetaObject(object, engine);
}
static bool isQuickStyleItemMetaObject(const QMetaObject *metaObject)
{
if (metaObject) {
if (metaObject->className() == QByteArrayLiteral("QQuickStyleItem"))
return true;
return isQuickStyleItemMetaObject(metaObject->superClass());
}
return false;
}
static bool isQuickStyleItem(QObject *object)
{
if (object)
return isQuickStyleItemMetaObject(object->metaObject());
return false;
}
// This is used in share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp
QObject *createPrimitive(const QString &typeName, int majorNumber, int minorNumber, QQmlContext *context)
{
@@ -357,12 +377,15 @@ void doComponentCompleteRecursive(QObject *object, NodeInstanceServer *nodeInsta
doComponentCompleteRecursive(child, nodeInstanceServer);
}
if (item) {
static_cast<QQmlParserStatus*>(item)->componentComplete();
} else {
QQmlParserStatus *qmlParserStatus = dynamic_cast< QQmlParserStatus*>(object);
if (qmlParserStatus)
qmlParserStatus->componentComplete();
if (!isQuickStyleItem(item)) {
qDebug() << Q_FUNC_INFO << item;
if (item) {
static_cast<QQmlParserStatus *>(item)->componentComplete();
} else {
QQmlParserStatus *qmlParserStatus = dynamic_cast<QQmlParserStatus *>(object);
if (qmlParserStatus)
qmlParserStatus->componentComplete();
}
}
}
}

View File

@@ -413,7 +413,7 @@ SecondColumnLayout {
id: transparentIndicator
icon: StudioTheme.Constants.transparent
pixelSize: StudioTheme.Values.myIconFontSize * 1.4
tooltip: qsTr("Transparent TODO")
tooltip: qsTr("Transparent")
onClicked: {
colorPicker.alpha = 0
colorPicker.updateColor()
@@ -575,22 +575,29 @@ SecondColumnLayout {
onRightMouseButtonClicked: contextMenu.popup(colorPicker)
onColorInvalidated: {
if (colorPicker.saturation > 0.0 && colorPicker.lightness > 0.0) {
hueSpinBox.value = colorPicker.hue
switch (colorPicker.mode) {
case ColorPicker.Mode.HSLA:
hslHueSpinBox.value = colorPicker.hue
hslSaturationSpinBox.value = colorPicker.saturationHSL
hslLightnessSpinBox.value = colorPicker.lightness
hslAlphaSpinBox.value = colorPicker.alpha
break
case ColorPicker.Mode.RGBA:
redSpinBox.value = (colorPicker.color.r * 255)
greenSpinBox.value = (colorPicker.color.g * 255)
blueSpinBox.value = (colorPicker.color.b * 255)
rgbAlphaSpinBox.value = (colorPicker.alpha * 255)
break
case ColorPicker.Mode.HSVA:
default:
hsvHueSpinBox.value = colorPicker.hue
hsvSaturationSpinBox.value = colorPicker.saturationHSV
hsvValueSpinBox.value = colorPicker.value
hsvAlphaSpinBox.value = colorPicker.alpha
break
}
if (colorPicker.lightness > 0.0)
saturationSpinBox.value = colorPicker.saturation
else
colorPicker.saturation = saturationSpinBox.value
lightnessSpinBox.value = colorPicker.lightness
hslaAlphaSpinBox.value = colorPicker.alpha
redSpinBox.value = (colorPicker.color.r * 255)
greenSpinBox.value = (colorPicker.color.g * 255)
blueSpinBox.value = (colorPicker.color.b * 255)
rgbaAlphaSpinBox.value = (colorPicker.alpha * 255)
}
}
@@ -766,29 +773,22 @@ SecondColumnLayout {
+ 4 * StudioTheme.Values.colorEditorPopupSpinBoxWidth
width: implicitWidth
actionIndicatorVisible: false
model: ["RGBA", "HSLA"]
onActivated: {
switch (colorMode.currentText) {
case "RGBA":
rgbaRow.visible = true
hslaRow.visible = false
break
case "HSLA":
rgbaRow.visible = false
hslaRow.visible = true
break
default:
console.log("Unknown color mode selected.")
rgbaRow.visible = true
hslaRow.visible = false
}
}
textRole: "text"
valueRole: "value"
model: [
{ value: ColorPicker.Mode.HSVA, text: "HSVA" },
{ value: ColorPicker.Mode.RGBA, text: "RGBA" },
{ value: ColorPicker.Mode.HSLA, text: "HSLA" }
]
onActivated: colorPicker.mode = colorMode.currentValue
}
}
RowLayout {
id: rgbaRow
visible: colorPicker.mode === ColorPicker.Mode.RGBA
Layout.fillWidth: true
spacing: StudioTheme.Values.controlGap
@@ -847,7 +847,7 @@ SecondColumnLayout {
}
DoubleSpinBox {
id: rgbaAlphaSpinBox
id: rgbAlphaSpinBox
width: StudioTheme.Values.colorEditorPopupSpinBoxWidth
stepSize: 1
@@ -856,7 +856,7 @@ SecondColumnLayout {
decimals: 0
onValueModified: {
var tmp = rgbaAlphaSpinBox.value / 255.0
var tmp = rgbAlphaSpinBox.value / 255.0
if (colorPicker.alpha !== tmp && !colorPicker.block) {
colorPicker.alpha = tmp
colorPicker.updateColor()
@@ -868,49 +868,109 @@ SecondColumnLayout {
RowLayout {
id: hslaRow
visible: false
visible: colorPicker.mode === ColorPicker.Mode.HSLA
Layout.fillWidth: true
spacing: StudioTheme.Values.controlGap
DoubleSpinBox {
id: hueSpinBox
id: hslHueSpinBox
width: StudioTheme.Values.colorEditorPopupSpinBoxWidth
onValueModified: {
if (colorPicker.hue !== hueSpinBox.value && !colorPicker.block) {
colorPicker.hue = hueSpinBox.value
if (colorPicker.hue !== hslHueSpinBox.value
&& !colorPicker.block) {
colorPicker.hue = hslHueSpinBox.value
colorPicker.updateColor()
}
}
}
DoubleSpinBox {
id: saturationSpinBox
id: hslSaturationSpinBox
width: StudioTheme.Values.colorEditorPopupSpinBoxWidth
onValueModified: {
if (colorPicker.saturation !== saturationSpinBox.value && !colorPicker.block) {
colorPicker.saturation = saturationSpinBox.value
if (colorPicker.saturationHSL !== hslSaturationSpinBox.value
&& !colorPicker.block) {
colorPicker.saturationHSL = hslSaturationSpinBox.value
colorPicker.updateColor()
}
}
}
DoubleSpinBox {
id: lightnessSpinBox
id: hslLightnessSpinBox
width: StudioTheme.Values.colorEditorPopupSpinBoxWidth
onValueModified: {
if (colorPicker.lightness !== lightnessSpinBox.value && !colorPicker.block) {
colorPicker.lightness = lightnessSpinBox.value
if (colorPicker.lightness !== hslLightnessSpinBox.value
&& !colorPicker.block) {
colorPicker.lightness = hslLightnessSpinBox.value
colorPicker.updateColor()
}
}
}
DoubleSpinBox {
id: hslaAlphaSpinBox
id: hslAlphaSpinBox
width: StudioTheme.Values.colorEditorPopupSpinBoxWidth
onValueModified: {
if (colorPicker.alpha !== hslaAlphaSpinBox.value && !colorPicker.block) {
colorPicker.alpha = hslaAlphaSpinBox.value
if (colorPicker.alpha !== hslAlphaSpinBox.value
&& !colorPicker.block) {
colorPicker.alpha = hslAlphaSpinBox.value
colorPicker.updateColor()
}
}
}
}
RowLayout {
id: hsvaRow
visible: colorPicker.mode === ColorPicker.Mode.HSVA
Layout.fillWidth: true
spacing: StudioTheme.Values.controlGap
DoubleSpinBox {
id: hsvHueSpinBox
width: StudioTheme.Values.colorEditorPopupSpinBoxWidth
onValueModified: {
if (colorPicker.hue !== hsvHueSpinBox.value
&& !colorPicker.block) {
colorPicker.hue = hsvHueSpinBox.value
colorPicker.updateColor()
}
}
}
DoubleSpinBox {
id: hsvSaturationSpinBox
width: StudioTheme.Values.colorEditorPopupSpinBoxWidth
onValueModified: {
if (colorPicker.saturationHSV !== hsvSaturationSpinBox.value
&& !colorPicker.block) {
colorPicker.saturationHSV = hsvSaturationSpinBox.value
colorPicker.updateColor()
}
}
}
DoubleSpinBox {
id: hsvValueSpinBox
width: StudioTheme.Values.colorEditorPopupSpinBoxWidth
onValueModified: {
if (colorPicker.value !== hsvValueSpinBox.value
&& !colorPicker.block) {
colorPicker.value = hsvValueSpinBox.value
colorPicker.updateColor()
}
}
}
DoubleSpinBox {
id: hsvAlphaSpinBox
width: StudioTheme.Values.colorEditorPopupSpinBoxWidth
onValueModified: {
if (colorPicker.alpha !== hsvAlphaSpinBox.value
&& !colorPicker.block) {
colorPicker.alpha = hsvAlphaSpinBox.value
colorPicker.updateColor()
}
}

View File

@@ -29,15 +29,26 @@ import StudioTheme 1.0 as StudioTheme
Column {
id: root
enum Mode {
HSVA,
RGBA,
HSLA
}
property int mode: ColorPicker.Mode.HSVA
property color color
property real alpha: 1
property real hue: 0
property real saturation: 0
property real saturationHSL: 0
property real saturationHSV: 0
property real lightness: 0
property real value: 0
property real alpha: 1
property bool achromatic: false
property int sliderMargins: 6
property bool block: false
signal updateColor
@@ -46,30 +57,84 @@ Column {
spacing: 10
onAlphaChanged: invalidateColor()
onSaturationChanged: invalidateColor()
onLightnessChanged: invalidateColor()
onHueChanged: invalidateColor()
onColorChanged: {
var myAlpha = root.color.a
rgbToHsl(root.color)
root.alpha = myAlpha
onModeChanged: {
switch (root.mode) {
case ColorPicker.Mode.RGBA:
root.color = Qt.rgba(root.color.r, root.color.g, root.color.b, root.alpha)
break
case ColorPicker.Mode.HSLA:
root.color = Qt.hsla(root.hue, root.saturationHSL, root.lightness, root.alpha)
break
case ColorPicker.Mode.HSVA:
default:
root.color = Qt.hsva(root.hue, root.saturationHSV, root.value, root.alpha)
break
}
gradientOverlay.requestPaint()
}
onHueChanged: {
if (root.mode === ColorPicker.Mode.HSLA)
root.color.hslHue = root.hue
else
root.color.hsvHue = root.hue
}
onSaturationHSLChanged: {
root.color.hslSaturation = root.saturationHSL
invalidateColor()
}
onSaturationHSVChanged: {
root.color.hsvSaturation = root.saturationHSV
}
onLightnessChanged: {
root.color.hslLightness = root.lightness
}
onValueChanged: {
root.color.hsvValue = root.value
}
onAlphaChanged: invalidateColor()
onColorChanged: invalidateColor()
function invalidateColor() {
if (root.block)
return
root.block = true
root.color = Qt.hsla(root.hue,
root.saturation,
root.lightness,
root.alpha)
if (root.color.hsvSaturation > 0.0
&& root.color.hsvValue > 0.0
&& root.color.hsvHue !== -1.0)
root.hue = root.color.hsvHue
if (root.saturation > 0.0 && root.lightness > 0.0)
hueSlider.value = root.hue
if (root.color.hslSaturation > 0.0
&& root.color.hslLightness > 0.0
&& root.color.hslHue !== -1.0)
root.hue = root.color.hslHue
if (root.color.hslLightness !== 0.0 && root.color.hslLightness !== 1.0 && !root.achromatic)
root.saturationHSL = root.color.hslSaturation
if (root.color.hsvValue !== 0.0 && root.color.hsvValue !== 1.0 && !root.achromatic)
root.saturationHSV = root.color.hsvSaturation
root.lightness = root.color.hslLightness
root.value = root.color.hsvValue
if (root.color.hslLightness === 0.0 || root.color.hslLightness === 1.0
|| root.color.hsvValue === 0.0 || root.color.hsvValue === 1.0
|| root.color.hsvHue === -1.0 || root.color.hslHue === -1.0)
root.achromatic = true
else
root.achromatic = false
if (root.mode === ColorPicker.Mode.HSLA)
root.color = Qt.hsla(root.hue, root.saturationHSL, root.lightness, root.alpha)
else
root.color = Qt.hsva(root.hue, root.saturationHSV, root.value, root.alpha)
luminanceSlider.value = (1.0 - root.value)
hueSlider.value = root.hue
opacitySlider.value = (1.0 - root.alpha)
root.colorInvalidated()
@@ -77,39 +142,51 @@ Column {
root.block = false
}
function rgbToHsl(color) {
var r = color.r
var g = color.g
var b = color.b
function drawHSVA(ctx) {
for (var row = 0; row < gradientOverlay.height; row++) {
var gradient = ctx.createLinearGradient(0, 0, gradientOverlay.width, 0)
var v = Math.abs(row - gradientOverlay.height) / gradientOverlay.height
var max = Math.max(r, g, b), min = Math.min(r, g, b)
var h, s, l = (max + min) / 2
gradient.addColorStop(0, Qt.hsva(root.hue, 0, v, 1))
gradient.addColorStop(1, Qt.hsva(root.hue, 1, v, 1))
if (max === min) {
h = 0
s = 0
} else {
var d = max - min
s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6
ctx.fillStyle = gradient
ctx.fillRect(0, row, gradientOverlay.width, 1)
}
}
root.block = true
function drawRGBA(ctx) {
var gradient = ctx.createLinearGradient(0, 0, gradientOverlay.width, 0)
gradient.addColorStop(0.000, Qt.rgba(1, 0, 0, 1))
gradient.addColorStop(0.167, Qt.rgba(1, 1, 0, 1))
gradient.addColorStop(0.333, Qt.rgba(0, 1, 0, 1))
gradient.addColorStop(0.500, Qt.rgba(0, 1, 1, 1))
gradient.addColorStop(0.667, Qt.rgba(0, 0, 1, 1))
gradient.addColorStop(0.833, Qt.rgba(1, 0, 1, 1))
gradient.addColorStop(1.000, Qt.rgba(1, 0, 0, 1))
if (s > 0)
root.hue = h
ctx.fillStyle = gradient
ctx.fillRect(0, 0, gradientOverlay.width, gradientOverlay.height)
root.saturation = s
root.lightness = l
gradient = ctx.createLinearGradient(0, 0, 0, gradientOverlay.height)
gradient.addColorStop(0.000, Qt.rgba(0, 0, 0, 0))
gradient.addColorStop(1.000, Qt.rgba(1, 1, 1, 1))
root.block = false
invalidateColor()
ctx.fillStyle = gradient
ctx.fillRect(0, 0, gradientOverlay.width, gradientOverlay.height)
}
function drawHSLA(ctx) {
for (var row = 0; row < gradientOverlay.height; row++) {
var gradient = ctx.createLinearGradient(0, 0, gradientOverlay.width, 0)
var l = Math.abs(row - gradientOverlay.height) / gradientOverlay.height
gradient.addColorStop(0, Qt.hsla(root.hue, 0, l, 1))
gradient.addColorStop(1, Qt.hsla(root.hue, 1, l, 1))
ctx.fillStyle = gradient
ctx.fillRect(0, row, gradientOverlay.width, 1)
}
}
Rectangle {
@@ -133,26 +210,30 @@ Column {
Canvas {
id: gradientOverlay
property real hue: root.hue
anchors.fill: parent
opacity: root.alpha
opacity: root.color.a
Connections {
target: root
function onHueChanged() { gradientOverlay.requestPaint() }
}
onHueChanged: requestPaint()
onPaint: {
var ctx = gradientOverlay.getContext('2d')
ctx.save()
ctx.clearRect(0, 0, gradientOverlay.width, gradientOverlay.height)
for (var row = 0; row < gradientOverlay.height; row++) {
var gradient = ctx.createLinearGradient(0, 0, gradientOverlay.width,0)
var l = Math.abs(row - gradientOverlay.height) / gradientOverlay.height
gradient.addColorStop(0, Qt.hsla(gradientOverlay.hue, 0, l, 1))
gradient.addColorStop(1, Qt.hsla(gradientOverlay.hue, 1, l, 1))
ctx.fillStyle = gradient
ctx.fillRect(0, row, gradientOverlay.width, 1)
switch (root.mode) {
case ColorPicker.Mode.RGBA:
root.drawRGBA(ctx)
break
case ColorPicker.Mode.HSLA:
root.drawHSLA(ctx)
break
case ColorPicker.Mode.HSVA:
default:
root.drawHSVA(ctx)
break
}
ctx.restore()
@@ -162,25 +243,41 @@ Column {
Canvas {
id: pickerCross
property real cavnasSaturation: root.saturation
property real canvasLightness: root.lightness
property color strokeStyle: "lightGray"
opacity: 0.8
anchors.fill: parent
antialiasing: true
onCavnasSaturationChanged: requestPaint();
onCanvasLightnessChanged: requestPaint();
Connections {
target: root
function onColorInvalidated() { pickerCross.requestPaint() }
function onColorChanged() { pickerCross.requestPaint() }
function onModeChanged() { pickerCross.requestPaint() }
}
onPaint: {
var ctx = pickerCross.getContext('2d')
ctx.save()
ctx.clearRect(0, 0, pickerCross.width, pickerCross.height)
var yy = pickerCross.height -root.lightness * pickerCross.height
var xx = root.saturation * pickerCross.width
var yy, xx = 0
switch (root.mode) {
case ColorPicker.Mode.RGBA:
yy = pickerCross.height - root.saturationHSV * pickerCross.height
xx = root.hue * pickerCross.width
break
case ColorPicker.Mode.HSLA:
yy = pickerCross.height - root.lightness * pickerCross.height
xx = root.saturationHSL * pickerCross.width
break
case ColorPicker.Mode.HSVA:
default:
yy = pickerCross.height - root.value * pickerCross.height
xx = root.saturationHSV * pickerCross.width
break
}
ctx.strokeStyle = pickerCross.strokeStyle
ctx.lineWidth = 1
@@ -200,24 +297,37 @@ Column {
}
MouseArea {
id: mapMouseArea
id: mouseArea
anchors.fill: parent
preventStealing: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
onPositionChanged: function(mouse) {
if (pressed && mouse.buttons === Qt.LeftButton) {
if (mouseArea.pressed && mouse.buttons === Qt.LeftButton) {
var xx = Math.max(0, Math.min(mouse.x, parent.width))
var yy = Math.max(0, Math.min(mouse.y, parent.height))
root.lightness = 1.0 - yy / parent.height
root.saturation = xx / parent.width
switch (root.mode) {
case ColorPicker.Mode.RGBA:
root.saturationHSV = 1.0 - yy / parent.height
root.hue = xx / parent.width
break
case ColorPicker.Mode.HSLA:
root.saturationHSL = xx / parent.width
root.lightness = 1.0 - yy / parent.height
break
case ColorPicker.Mode.HSVA:
default:
root.saturationHSV = xx / parent.width
root.value = 1.0 - yy / parent.height
break
}
}
}
onPressed: function(mouse) {
if (mouse.button === Qt.LeftButton)
positionChanged(mouse)
mouseArea.positionChanged(mouse)
}
onReleased: function(mouse) {
if (mouse.button === Qt.LeftButton)
@@ -233,10 +343,23 @@ Column {
HueSlider {
id: hueSlider
visible: root.mode !== ColorPicker.Mode.RGBA
width: parent.width
onValueChanged: {
if (root.hue !== value)
root.hue = value
if (root.hue !== hueSlider.value)
root.hue = hueSlider.value
}
onClicked: root.updateColor()
}
LuminanceSlider {
id: luminanceSlider
visible: root.mode === ColorPicker.Mode.RGBA
width: parent.width
color: Qt.hsva(root.hue, root.color.hsvSaturation, 1, 1)
onValueChanged: {
if (root.value !== luminanceSlider.value)
root.value = (1.0 - luminanceSlider.value)
}
onClicked: root.updateColor()
}
@@ -246,8 +369,8 @@ Column {
width: parent.width
color: Qt.rgba(root.color.r, root.color.g, root.color.b, 1)
onValueChanged: {
if (root.alpha !== value)
root.alpha = (1.0 - value)
if (root.alpha !== opacitySlider.value)
root.alpha = (1.0 - opacitySlider.value)
}
onClicked: root.updateColor()
}

View File

@@ -0,0 +1,121 @@
/****************************************************************************
**
** Copyright (C) 2021 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 StudioTheme 1.0 as StudioTheme
Item {
id: root
property real value: 1
property real minimum: 0
property real maximum: 1
property bool pressed: mouseArea.pressed
property bool integer: false
property color color
signal clicked
height: StudioTheme.Values.hueSliderHeight
function updatePos() {
if (root.maximum > root.minimum) {
var pos = (track.width - handle.width) * (root.value - root.minimum) / (root.maximum - root.minimum)
return Math.min(Math.max(pos, 0), track.width - handle.width)
} else {
return 0
}
}
Item {
id: track
width: parent.width
height: parent.height
Image {
id: checkerboard
anchors.fill: parent
source: "images/checkers.png"
fillMode: Image.Tile
}
Rectangle {
anchors.fill: parent
border.color: StudioTheme.Values.themeControlOutline
border.width: StudioTheme.Values.border
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.000; color: root.color }
GradientStop { position: 1.000; color: "black" }
}
}
Rectangle {
id: handle
width: StudioTheme.Values.hueSliderHandleWidth
height: track.height - 4
anchors.verticalCenter: parent.verticalCenter
smooth: true
color: "transparent"
radius: 2
border.color: "black"
border.width: 1
x: root.updatePos()
y: 2
z: 1
Rectangle {
anchors.fill: parent
anchors.margins: 1
color: "transparent"
radius: 1
border.color: "white"
border.width: 1
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
preventStealing: true
function calculateValue() {
var handleX = Math.max(0, Math.min(mouseArea.mouseX, mouseArea.width))
var realValue = (root.maximum - root.minimum) * handleX / mouseArea.width + root.minimum
root.value = root.integer ? Math.round(realValue) : realValue
}
onPressed: calculateValue()
onReleased: root.clicked()
onPositionChanged: {
if (pressed)
calculateValue()
}
}
}
}

View File

@@ -45,6 +45,7 @@ Label 2.0 Label.qml
LineEdit 2.0 LineEdit.qml
LinkIndicator2D 2.0 LinkIndicator2D.qml
ListViewComboBox 2.0 ListViewComboBox.qml
LuminanceSlider 2.0 LuminanceSlider.qml
MarginSection 2.0 MarginSection.qml
MultiIconLabel 2.0 MultiIconLabel.qml
OpacitySlider 2.0 OpacitySlider.qml

View File

@@ -52,6 +52,7 @@ add_qtc_library(Utils
filecrumblabel.cpp filecrumblabel.h
fileinprojectfinder.cpp fileinprojectfinder.h
filenamevalidatinglineedit.cpp filenamevalidatinglineedit.h
filepath.cpp filepath.h
filesearch.cpp filesearch.h
filesystemwatcher.cpp filesystemwatcher.h
fileutils.cpp fileutils.h

1295
src/libs/utils/filepath.cpp Normal file

File diff suppressed because it is too large Load Diff

193
src/libs/utils/filepath.h Normal file
View File

@@ -0,0 +1,193 @@
/****************************************************************************
**
** Copyright (C) 2021 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.
**
****************************************************************************/
#pragma once
#include "utils_global.h"
#include "hostosinfo.h"
#include <QDir>
#include <QMetaType>
#include <functional>
#include <memory>
QT_BEGIN_NAMESPACE
class QDateTime;
class QDebug;
class QFileInfo;
class QUrl;
QT_END_NAMESPACE
class tst_fileutils; // This becomes a friend of Utils::FilePath for testing private methods.
namespace Utils {
class Environment;
class QTCREATOR_UTILS_EXPORT FilePath
{
public:
FilePath();
static FilePath fromString(const QString &filepath);
static FilePath fromFileInfo(const QFileInfo &info);
static FilePath fromStringWithExtension(const QString &filepath, const QString &defaultExtension);
static FilePath fromUserInput(const QString &filepath);
static FilePath fromUtf8(const char *filepath, int filepathSize = -1);
static FilePath fromVariant(const QVariant &variant);
QString toString() const;
FilePath onDevice(const FilePath &deviceTemplate) const;
FilePath withNewPath(const QString &newPath) const;
QFileInfo toFileInfo() const;
QVariant toVariant() const;
QDir toDir() const;
QString toUserOutput() const;
QString shortNativePath() const;
QString fileName() const;
QString fileNameWithPathComponents(int pathComponents) const;
QString baseName() const;
QString completeBaseName() const;
QString suffix() const;
QString completeSuffix() const;
QString scheme() const { return m_scheme; }
void setScheme(const QString &scheme);
QString host() const { return m_host; }
void setHost(const QString &host);
QString path() const { return m_data; }
void setPath(const QString &path) { m_data = path; }
bool needsDevice() const;
bool exists() const;
bool isWritablePath() const { return isWritableDir(); } // Remove.
bool isWritableDir() const;
bool isWritableFile() const;
bool ensureWritableDir() const;
bool ensureExistingFile() const;
bool isExecutableFile() const;
bool isReadableFile() const;
bool isReadableDir() const;
bool isRelativePath() const;
bool isAbsolutePath() const { return !isRelativePath(); }
bool isFile() const;
bool isDir() const;
bool createDir() const;
QList<FilePath> dirEntries(const QStringList &nameFilters,
QDir::Filters filters,
QDir::SortFlags sort = QDir::NoSort) const;
QList<FilePath> dirEntries(QDir::Filters filters) const;
QByteArray fileContents(qint64 maxSize = -1, qint64 offset = 0) const;
bool writeFileContents(const QByteArray &data) const;
FilePath parentDir() const;
FilePath absolutePath() const;
FilePath absoluteFilePath() const;
FilePath absoluteFilePath(const FilePath &tail) const;
FilePath absoluteFromRelativePath(const FilePath &anchor) const;
bool operator==(const FilePath &other) const;
bool operator!=(const FilePath &other) const;
bool operator<(const FilePath &other) const;
bool operator<=(const FilePath &other) const;
bool operator>(const FilePath &other) const;
bool operator>=(const FilePath &other) const;
FilePath operator+(const QString &s) const;
bool isChildOf(const FilePath &s) const;
bool isChildOf(const QDir &dir) const;
bool startsWith(const QString &s) const;
bool endsWith(const QString &s) const;
bool isNewerThan(const QDateTime &timeStamp) const;
QDateTime lastModified() const;
QFile::Permissions permissions() const;
OsType osType() const;
bool removeFile() const;
bool removeRecursively(QString *error = nullptr) const;
bool copyFile(const FilePath &target) const;
bool renameFile(const FilePath &target) const;
Qt::CaseSensitivity caseSensitivity() const;
FilePath relativeChildPath(const FilePath &parent) const;
FilePath relativePath(const FilePath &anchor) const;
FilePath pathAppended(const QString &str) const;
FilePath stringAppended(const QString &str) const;
FilePath resolvePath(const QString &fileName) const;
FilePath cleanPath() const;
FilePath canonicalPath() const;
FilePath symLinkTarget() const;
FilePath resolveSymlinks() const;
FilePath withExecutableSuffix() const;
FilePath operator/(const QString &str) const;
void clear();
bool isEmpty() const;
uint hash(uint seed) const;
// NOTE: Most FilePath operations on FilePath created from URL currently
// do not work. Among the working are .toVariant() and .toUrl().
static FilePath fromUrl(const QUrl &url);
QUrl toUrl() const;
FilePath searchOnDevice(const QList<FilePath> &dirs) const;
Environment deviceEnvironment() const;
static QString formatFilePaths(const QList<FilePath> &files, const QString &separator);
static void removeDuplicates(QList<FilePath> &files);
static void sort(QList<FilePath> &files);
private:
friend class ::tst_fileutils;
static QString calcRelativePath(const QString &absolutePath, const QString &absoluteAnchorPath);
QString m_scheme;
QString m_host;
QString m_data;
};
using FilePaths = QList<FilePath>;
} // namespace Utils
QT_BEGIN_NAMESPACE
QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug dbg, const Utils::FilePath &c);
QT_END_NAMESPACE
Q_DECLARE_METATYPE(Utils::FilePath)

File diff suppressed because it is too large Load Diff

View File

@@ -27,6 +27,7 @@
#include "utils_global.h"
#include "filepath.h"
#include "hostosinfo.h"
#include <QCoreApplication>
@@ -40,33 +41,17 @@
#include <functional>
#include <memory>
namespace Utils {
class Environment;
class FilePath;
} // Utils
QT_BEGIN_NAMESPACE
class QDataStream;
class QDateTime;
class QDir;
class QFile;
class QFileInfo;
class QTemporaryFile;
class QTextStream;
class QWidget;
QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug dbg, const Utils::FilePath &c);
// for withNtfsPermissions
#ifdef Q_OS_WIN
extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
#endif
QT_END_NAMESPACE
// tst_fileutils becomes a friend of Utils::FilePath for testing private method
class tst_fileutils;
namespace Utils {
class DeviceFileHooks
@@ -99,152 +84,6 @@ public:
std::function<Environment(const FilePath &)> environment;
};
class QTCREATOR_UTILS_EXPORT FilePath
{
public:
FilePath();
static FilePath fromString(const QString &filepath);
static FilePath fromFileInfo(const QFileInfo &info);
static FilePath fromStringWithExtension(const QString &filepath, const QString &defaultExtension);
static FilePath fromUserInput(const QString &filepath);
static FilePath fromUtf8(const char *filepath, int filepathSize = -1);
static FilePath fromVariant(const QVariant &variant);
QString toString() const;
FilePath onDevice(const FilePath &deviceTemplate) const;
FilePath withNewPath(const QString &newPath) const;
QFileInfo toFileInfo() const;
QVariant toVariant() const;
QDir toDir() const;
QString toUserOutput() const;
QString shortNativePath() const;
QString fileName() const;
QString fileNameWithPathComponents(int pathComponents) const;
QString baseName() const;
QString completeBaseName() const;
QString suffix() const;
QString completeSuffix() const;
QString scheme() const { return m_scheme; }
void setScheme(const QString &scheme);
QString host() const { return m_host; }
void setHost(const QString &host);
QString path() const { return m_data; }
void setPath(const QString &path) { m_data = path; }
bool needsDevice() const;
bool exists() const;
bool isWritablePath() const { return isWritableDir(); } // Remove.
bool isWritableDir() const;
bool isWritableFile() const;
bool ensureWritableDir() const;
bool ensureExistingFile() const;
bool isExecutableFile() const;
bool isReadableFile() const;
bool isReadableDir() const;
bool isRelativePath() const;
bool isAbsolutePath() const { return !isRelativePath(); }
bool isFile() const;
bool isDir() const;
bool createDir() const;
QList<FilePath> dirEntries(const QStringList &nameFilters,
QDir::Filters filters,
QDir::SortFlags sort = QDir::NoSort) const;
QList<FilePath> dirEntries(QDir::Filters filters) const;
QByteArray fileContents(qint64 maxSize = -1, qint64 offset = 0) const;
bool writeFileContents(const QByteArray &data) const;
FilePath parentDir() const;
FilePath absolutePath() const;
FilePath absoluteFilePath() const;
FilePath absoluteFilePath(const FilePath &tail) const;
FilePath absoluteFromRelativePath(const FilePath &anchor) const;
bool operator==(const FilePath &other) const;
bool operator!=(const FilePath &other) const;
bool operator<(const FilePath &other) const;
bool operator<=(const FilePath &other) const;
bool operator>(const FilePath &other) const;
bool operator>=(const FilePath &other) const;
FilePath operator+(const QString &s) const;
bool isChildOf(const FilePath &s) const;
bool isChildOf(const QDir &dir) const;
bool startsWith(const QString &s) const;
bool endsWith(const QString &s) const;
bool isNewerThan(const QDateTime &timeStamp) const;
QDateTime lastModified() const;
QFile::Permissions permissions() const;
OsType osType() const;
bool removeFile() const;
bool removeRecursively(QString *error = nullptr) const;
bool copyFile(const FilePath &target) const;
bool renameFile(const FilePath &target) const;
Qt::CaseSensitivity caseSensitivity() const;
FilePath relativeChildPath(const FilePath &parent) const;
FilePath relativePath(const FilePath &anchor) const;
FilePath pathAppended(const QString &str) const;
FilePath stringAppended(const QString &str) const;
FilePath resolvePath(const QString &fileName) const;
FilePath resolveSymlinkTarget() const;
FilePath cleanPath() const;
FilePath canonicalPath() const;
FilePath symLinkTarget() const;
FilePath resolveSymlinks() const;
FilePath withExecutableSuffix() const;
FilePath operator/(const QString &str) const;
void clear();
bool isEmpty() const;
uint hash(uint seed) const;
// NOTE: Most FilePath operations on FilePath created from URL currently
// do not work. Among the working are .toVariant() and .toUrl().
static FilePath fromUrl(const QUrl &url);
QUrl toUrl() const;
static void setDeviceFileHooks(const DeviceFileHooks &hooks);
FilePath searchOnDevice(const QList<FilePath> &dirs) const;
Environment deviceEnvironment() const;
static QString formatFilePaths(const QList<FilePath> &files, const QString &separator);
static void removeDuplicates(QList<FilePath> &files);
static void sort(QList<FilePath> &files);
static QList<FilePath> filterEntriesHelper(const FilePath &base,
const QStringList &entries,
const QStringList &nameFilters,
QDir::Filters filters,
QDir::SortFlags sort);
private:
friend class ::tst_fileutils;
static QString calcRelativePath(const QString &absolutePath, const QString &absoluteAnchorPath);
QString m_scheme;
QString m_host;
QString m_data;
};
QTCREATOR_UTILS_EXPORT QTextStream &operator<<(QTextStream &s, const FilePath &fn);
using FilePaths = QList<FilePath>;
class QTCREATOR_UTILS_EXPORT FileUtils {
public:
#ifdef QT_GUI_LIB
@@ -287,6 +126,8 @@ public:
static QByteArray fileId(const FilePath &fileName);
static FilePath homePath();
static bool renameFile(const FilePath &srcFilePath, const FilePath &tgtFilePath);
static void setDeviceFileHooks(const DeviceFileHooks &hooks);
};
template<typename T>
@@ -432,6 +273,8 @@ private:
bool m_autoRemove = true;
};
QTCREATOR_UTILS_EXPORT QTextStream &operator<<(QTextStream &s, const FilePath &fn);
inline uint qHash(const Utils::FilePath &a, uint seed = 0) { return a.hash(seed); }
} // namespace Utils
@@ -445,4 +288,3 @@ template<> struct QTCREATOR_UTILS_EXPORT hash<Utils::FilePath>
};
} // namespace std
Q_DECLARE_METATYPE(Utils::FilePath)

View File

@@ -554,7 +554,13 @@ void QtcProcess::start()
{
d->clearForRun();
QTC_CHECK(d->m_writeData.isEmpty()); // FIXME: Use it.
if (!d->m_writeData.isEmpty()) {
connect(d->m_process, &ProcessInterface::started, this, [this] {
const qint64 bytesWritten = write(d->m_writeData);
QTC_CHECK(bytesWritten == d->m_writeData.size());
closeWriteChannel(); // FIXME: Is this good?
});
}
if (d->m_commandLine.executable().needsDevice()) {
QTC_ASSERT(s_deviceHooks.startProcessHook, return);
@@ -1219,6 +1225,7 @@ void QtcProcess::setExitCodeInterpreter(const ExitCodeInterpreter &interpreter)
void QtcProcess::setWriteData(const QByteArray &writeData)
{
d->m_writeData = writeData;
setKeepWriteChannelOpen();
}
#ifdef QT_GUI_LIB
@@ -1231,11 +1238,9 @@ static bool isGuiThread()
void QtcProcess::runBlocking()
{
// FIXME: Implement properly
if (d->m_commandLine.executable().needsDevice()) {
// writeData ?
QtcProcess::start();
waitForFinished();
return;
};

View File

@@ -62,6 +62,7 @@ SOURCES += \
$$PWD/fancylineedit.cpp \
$$PWD/qtcolorbutton.cpp \
$$PWD/savefile.cpp \
$$PWD/filepath.cpp \
$$PWD/fileutils.cpp \
$$PWD/textfileformat.cpp \
$$PWD/consoleprocess.cpp \
@@ -196,6 +197,7 @@ HEADERS += \
$$PWD/qtcolorbutton.h \
$$PWD/consoleprocess.h \
$$PWD/savefile.h \
$$PWD/filepath.h \
$$PWD/fileutils.h \
$$PWD/textfileformat.h \
$$PWD/uncommentselection.h \

View File

@@ -115,6 +115,8 @@ Project {
"fileinprojectfinder.h",
"filenamevalidatinglineedit.cpp",
"filenamevalidatinglineedit.h",
"filepath.cpp",
"filepath.h",
"filesearch.cpp",
"filesearch.h",
"filesystemwatcher.cpp",

View File

@@ -1066,13 +1066,20 @@ AndroidDeviceInfo AndroidConfigurations::showDeviceDialog(Project *project,
if (!serialNumber.isEmpty())
break;
}
const AndroidDeviceInfo defaultDevice = AndroidDeviceDialog::defaultDeviceInfo(serialNumber);
if (defaultDevice.isValid())
return defaultDevice;
AndroidDeviceDialog dialog(apiLevel, abis, serialNumber, Core::ICore::dialogParent());
AndroidDeviceInfo info = dialog.device();
AndroidDeviceInfo info = dialog.showAndGetSelectedDevice();
if (dialog.saveDeviceSelection() && info.isValid()) {
const QString serialNumber = info.type == AndroidDeviceInfo::Hardware ?
const QString newSerialNumber = info.type == AndroidDeviceInfo::Hardware ?
info.serialNumber : info.avdname;
if (!serialNumber.isEmpty())
AndroidConfigurations::setDefaultDevice(project, AndroidManager::devicePreferredAbi(info.cpuAbi, abis), serialNumber);
if (!newSerialNumber.isEmpty()) {
const QString preferredAbi = AndroidManager::devicePreferredAbi(info.cpuAbi, abis);
AndroidConfigurations::setDefaultDevice(project, preferredAbi, newSerialNumber);
}
}
return info;
}

View File

@@ -44,6 +44,8 @@ using namespace Android::Internal;
namespace Android {
namespace Internal {
QVector<AndroidDeviceInfo> AndroidDeviceDialog::m_connectedDevices = {};
// yeah, writing tree models is fun!
class AndroidDeviceModelNode
{
@@ -481,26 +483,30 @@ AndroidDeviceDialog::~AndroidDeviceDialog()
delete m_ui;
}
AndroidDeviceInfo AndroidDeviceDialog::device()
AndroidDeviceInfo AndroidDeviceDialog::defaultDeviceInfo(const QString &serialNumber)
{
AndroidDeviceDialog::updateConnectedDevicesList();
if (serialNumber.isEmpty())
return {};
return Utils::findOrDefault(m_connectedDevices, [serialNumber](const AndroidDeviceInfo &info) {
return info.serialNumber == serialNumber || info.avdname == serialNumber;
});
}
AndroidDeviceInfo AndroidDeviceDialog::showAndGetSelectedDevice()
{
auto dev = defaultDeviceInfo(m_defaultDevice);
if (dev.isValid())
return dev;
refreshDeviceList();
if (!m_defaultDevice.isEmpty()) {
auto device = std::find_if(m_connectedDevices.cbegin(),
m_connectedDevices.cend(),
[this](const AndroidDeviceInfo &info) {
return info.serialNumber == m_defaultDevice ||
info.avdname == m_defaultDevice;
});
if (device != m_connectedDevices.cend())
return *device;
m_defaultDevice.clear();
}
if (exec() == QDialog::Accepted)
return m_model->device(m_ui->deviceView->currentIndex());
return AndroidDeviceInfo();
return {};
}
bool AndroidDeviceDialog::saveDeviceSelection() const
@@ -508,11 +514,16 @@ bool AndroidDeviceDialog::saveDeviceSelection() const
return m_ui->defaultDeviceCheckBox->isChecked();
}
void AndroidDeviceDialog::updateConnectedDevicesList()
{
m_connectedDevices = AndroidConfig::connectedDevices(AndroidConfigurations::currentConfig()
.adbToolPath());
}
void AndroidDeviceDialog::refreshDeviceList()
{
m_ui->refreshDevicesButton->setEnabled(false);
m_progressIndicator->show();
m_connectedDevices = AndroidConfig::connectedDevices(AndroidConfigurations::currentConfig().adbToolPath());
m_futureWatcherRefreshDevices.setFuture(m_avdManager->avdList());
}

View File

@@ -56,7 +56,8 @@ public:
const QString &serialNumber, QWidget *parent = nullptr);
~AndroidDeviceDialog() override;
AndroidDeviceInfo device();
AndroidDeviceInfo showAndGetSelectedDevice();
static AndroidDeviceInfo defaultDeviceInfo(const QString &serialNumber);
bool saveDeviceSelection() const;
@@ -68,6 +69,7 @@ private:
void devicesRefreshed();
void enableOkayButton();
void defaultDeviceClear();
static void updateConnectedDevicesList();
AndroidDeviceModel *m_model;
Ui::AndroidDeviceDialog *m_ui;
@@ -76,8 +78,8 @@ private:
QStringList m_abis;
QString m_avdNameFromAdd;
QString m_defaultDevice;
static QVector<AndroidDeviceInfo> m_connectedDevices;
std::unique_ptr<AndroidAvdManager> m_avdManager;
QVector<AndroidDeviceInfo> m_connectedDevices;
QFutureWatcher<CreateAvdInfo> m_futureWatcherAddDevice;
QFutureWatcher<AndroidDeviceInfoList> m_futureWatcherRefreshDevices;
};

View File

@@ -515,7 +515,7 @@ void CMakeBuildSettingsWidget::batchEditConfiguration()
[expander](const QString &s) {
return expander->expand(s);
});
const CMakeConfig config = CMakeConfigItem::itemsFromArguments(expandedLines);
const CMakeConfig config = CMakeConfig::fromArguments(expandedLines);
m_configModel->setBatchEditConfiguration(config);
});
@@ -1048,7 +1048,7 @@ bool CMakeBuildConfiguration::fromMap(const QVariantMap &map)
}();
if (initialCMakeArguments().isEmpty()) {
QStringList initialArgs = defaultInitialCMakeArguments(kit(), buildTypeName)
+ Utils::transform(conf, [this](const CMakeConfigItem &i) {
+ Utils::transform(conf.toList(), [this](const CMakeConfigItem &i) {
return i.toArgument(macroExpander());
});
@@ -1110,7 +1110,8 @@ CMakeConfig CMakeBuildConfiguration::configurationChanges() const
QStringList CMakeBuildConfiguration::configurationChangesArguments() const
{
return Utils::transform(m_configurationChanges, [](const CMakeConfigItem &i) { return i.toArgument(); });
return Utils::transform(m_configurationChanges.toList(),
[](const CMakeConfigItem &i) { return i.toArgument(); });
}
QStringList CMakeBuildConfiguration::initialCMakeArguments() const
@@ -1296,9 +1297,9 @@ BuildInfo CMakeBuildConfigurationFactory::createBuildInfo(BuildType buildType)
BuildConfiguration::BuildType CMakeBuildConfiguration::buildType() const
{
QByteArray cmakeBuildTypeName = CMakeConfigItem::valueOf("CMAKE_BUILD_TYPE", m_configurationFromCMake);
QByteArray cmakeBuildTypeName = m_configurationFromCMake.valueOf("CMAKE_BUILD_TYPE");
if (cmakeBuildTypeName.isEmpty()) {
QByteArray cmakeCfgTypes = CMakeConfigItem::valueOf("CMAKE_CONFIGURATION_TYPES", m_configurationFromCMake);
QByteArray cmakeCfgTypes = m_configurationFromCMake.valueOf("CMAKE_CONFIGURATION_TYPES");
if (!cmakeCfgTypes.isEmpty())
cmakeBuildTypeName = cmakeBuildType().toUtf8();
}
@@ -1352,14 +1353,14 @@ QString CMakeBuildConfiguration::cmakeBuildType() const
QString errorMessage;
config = CMakeBuildSystem::parseCMakeCacheDotTxt(cmakeCacheTxt, &errorMessage);
} else {
config = CMakeConfigItem::itemsFromArguments(initialCMakeArguments());
config = CMakeConfig::fromArguments(initialCMakeArguments());
}
} else if (!hasCMakeCache) {
config = CMakeConfigItem::itemsFromArguments(initialCMakeArguments());
config = CMakeConfig::fromArguments(initialCMakeArguments());
}
if (!config.isEmpty() && !isMultiConfig()) {
cmakeBuildType = CMakeConfigItem::stringValueOf("CMAKE_BUILD_TYPE", config);
cmakeBuildType = config.stringValueOf("CMAKE_BUILD_TYPE");
const_cast<CMakeBuildConfiguration*>(this)->setCMakeBuildType(cmakeBuildType);
}

View File

@@ -838,7 +838,7 @@ void CMakeBuildSystem::wireUpConnections()
QString errorMessage;
const CMakeConfig config = CMakeBuildSystem::parseCMakeCacheDotTxt(cmakeCacheTxt, &errorMessage);
if (!config.isEmpty() && errorMessage.isEmpty()) {
QString cmakeBuildTypeName = CMakeConfigItem::stringValueOf("CMAKE_BUILD_TYPE", config);
QString cmakeBuildTypeName = config.stringValueOf("CMAKE_BUILD_TYPE");
cmakeBuildConfiguration()->setCMakeBuildType(cmakeBuildTypeName, true);
}
}
@@ -1057,7 +1057,7 @@ CMakeConfig CMakeBuildSystem::parseCMakeCacheDotTxt(const Utils::FilePath &cache
*errorMessage = tr("CMakeCache.txt file not found.");
return {};
}
CMakeConfig result = CMakeConfigItem::itemsFromFile(cacheFile, errorMessage);
CMakeConfig result = CMakeConfig::fromFile(cacheFile, errorMessage);
if (!errorMessage->isEmpty())
return {};
return result;
@@ -1210,7 +1210,7 @@ void CMakeBuildSystem::updateQmlJSCodeModel(const QStringList &extraHeaderPaths,
};
const CMakeConfig &cm = cmakeBuildConfiguration()->configurationFromCMake();
addImports(CMakeConfigItem::stringValueOf("QML_IMPORT_PATH", cm));
addImports(cm.stringValueOf("QML_IMPORT_PATH"));
addImports(kit()->value(QtSupport::KitQmlImportPath::id()).toString());
for (const QString &extraHeaderPath : extraHeaderPaths)
@@ -1244,7 +1244,7 @@ void CMakeBuildSystem::updateInitialCMakeExpandableVars()
{
const CMakeConfig &cm = cmakeBuildConfiguration()->configurationFromCMake();
const CMakeConfig &initialConfig =
CMakeConfigItem::itemsFromArguments(cmakeBuildConfiguration()->initialCMakeArguments());
CMakeConfig::fromArguments(cmakeBuildConfiguration()->initialCMakeArguments());
CMakeConfig config;
@@ -1275,7 +1275,7 @@ void CMakeBuildSystem::updateInitialCMakeExpandableVars()
});
if (it != cm.cend()) {
const QByteArray initialValue = CMakeConfigItem::expandedValueOf(kit(), var, initialConfig).toUtf8();
const QByteArray initialValue = initialConfig.expandedValueOf(kit(), var).toUtf8();
const FilePath initialPath = FilePath::fromString(QString::fromUtf8(initialValue));
const FilePath path = FilePath::fromString(QString::fromUtf8(it->value));
@@ -1299,20 +1299,23 @@ void CMakeBuildSystem::updateInitialCMakeExpandableVars()
});
if (it != cm.cend()) {
const QByteArray initialValue = CMakeConfigItem::expandedValueOf(kit(), var, initialConfig).toUtf8();
const FilePath initialPath = FilePath::fromString(QString::fromUtf8(initialValue));
const QByteArrayList initialValueList = initialConfig.expandedValueOf(kit(), var).toUtf8().split(';');
const bool pathIsContained
= Utils::contains(it->value.split(';'), [samePath, initialPath](const QByteArray &p) {
return samePath(FilePath::fromString(QString::fromUtf8(p)), initialPath);
});
if (!initialValue.isEmpty() && !pathIsContained) {
CMakeConfigItem item(*it);
item.value = initialValue;
item.value.append(";");
item.value.append(it->value);
for (const auto &initialValue: initialValueList) {
const FilePath initialPath = FilePath::fromString(QString::fromUtf8(initialValue));
config << item;
const bool pathIsContained
= Utils::contains(it->value.split(';'), [samePath, initialPath](const QByteArray &p) {
return samePath(FilePath::fromString(QString::fromUtf8(p)), initialPath);
});
if (!initialValue.isEmpty() && !pathIsContained) {
CMakeConfigItem item(*it);
item.value = initialValue;
item.value.append(";");
item.value.append(it->value);
config << item;
}
}
}
}

View File

@@ -55,29 +55,28 @@ CMakeConfigItem::CMakeConfigItem(const QByteArray &k, const QByteArray &v) :
key(k), value(v)
{ }
QByteArray CMakeConfigItem::valueOf(const QByteArray &key, const QList<CMakeConfigItem> &input)
QByteArray CMakeConfig::valueOf(const QByteArray &key) const
{
for (auto it = input.constBegin(); it != input.constEnd(); ++it) {
for (auto it = constBegin(); it != constEnd(); ++it) {
if (it->key == key)
return it->value;
}
return QByteArray();
}
QString CMakeConfigItem::stringValueOf(const QByteArray &key, const QList<CMakeConfigItem> &input)
QString CMakeConfig::stringValueOf(const QByteArray &key) const
{
return QString::fromUtf8(valueOf(key, input));
return QString::fromUtf8(valueOf(key));
}
FilePath CMakeConfigItem::filePathValueOf(const QByteArray &key, const QList<CMakeConfigItem> &input)
FilePath CMakeConfig::filePathValueOf(const QByteArray &key) const
{
return FilePath::fromUtf8(valueOf(key, input));
return FilePath::fromUtf8(valueOf(key));
}
QString CMakeConfigItem::expandedValueOf(const ProjectExplorer::Kit *k, const QByteArray &key,
const QList<CMakeConfigItem> &input)
QString CMakeConfig::expandedValueOf(const ProjectExplorer::Kit *k, const QByteArray &key) const
{
for (auto it = input.constBegin(); it != input.constEnd(); ++it) {
for (auto it = constBegin(); it != constEnd(); ++it) {
if (it->key == key)
return it->expandedValue(k);
}
@@ -312,7 +311,7 @@ static CMakeConfigItem unsetItemFromString(const QString &input)
return item;
}
QList<CMakeConfigItem> CMakeConfigItem::itemsFromArguments(const QStringList &list)
CMakeConfig CMakeConfig::fromArguments(const QStringList &list)
{
CMakeConfig result;
bool inSet = false;
@@ -348,7 +347,7 @@ QList<CMakeConfigItem> CMakeConfigItem::itemsFromArguments(const QStringList &li
return result;
}
QList<CMakeConfigItem> CMakeConfigItem::itemsFromFile(const Utils::FilePath &cacheFile, QString *errorMessage)
CMakeConfig CMakeConfig::fromFile(const Utils::FilePath &cacheFile, QString *errorMessage)
{
CMakeConfig result;
QFile cache(cacheFile.toString());
@@ -473,6 +472,11 @@ bool CMakeConfigItem::operator==(const CMakeConfigItem &o) const
return o.key == key && o.value == value && o.isUnset == isUnset;
}
uint qHash(const CMakeConfigItem &it)
{
return ::qHash(it.key) ^ ::qHash(it.value) ^ ::qHash(it.isUnset);
}
#if WITH_TESTS
} // namespace CMakeProjectManager

View File

@@ -43,18 +43,14 @@ class Kit;
namespace CMakeProjectManager {
class CMAKE_EXPORT CMakeConfigItem {
class CMAKE_EXPORT CMakeConfigItem
{
public:
enum Type { FILEPATH, PATH, BOOL, STRING, INTERNAL, STATIC, UNINITIALIZED };
CMakeConfigItem();
CMakeConfigItem(const QByteArray &k, Type t, const QByteArray &d, const QByteArray &v, const QStringList &s = {});
CMakeConfigItem(const QByteArray &k, const QByteArray &v);
static QByteArray valueOf(const QByteArray &key, const QList<CMakeConfigItem> &input);
static QString stringValueOf(const QByteArray &key, const QList<CMakeConfigItem> &input);
static Utils::FilePath filePathValueOf(const QByteArray &key, const QList<CMakeConfigItem> &input);
static QString expandedValueOf(const ProjectExplorer::Kit *k, const QByteArray &key,
const QList<CMakeConfigItem> &input);
static QStringList cmakeSplitValue(const QString &in, bool keepEmpty = false);
static Type typeStringToType(const QByteArray &typeString);
static QString typeToTypeString(const Type t);
@@ -66,8 +62,6 @@ public:
static bool less(const CMakeConfigItem &a, const CMakeConfigItem &b);
static CMakeConfigItem fromString(const QString &s);
static QList<CMakeConfigItem> itemsFromArguments(const QStringList &list);
static QList<CMakeConfigItem> itemsFromFile(const Utils::FilePath &input, QString *errorMessage);
QString toString(const Utils::MacroExpander *expander = nullptr) const;
QString toArgument(const Utils::MacroExpander *expander = nullptr) const;
QString toCMakeSetLine(const Utils::MacroExpander *expander = nullptr) const;
@@ -83,6 +77,25 @@ public:
QByteArray documentation;
QStringList values;
};
using CMakeConfig = QList<CMakeConfigItem>;
uint qHash(const CMakeConfigItem &it); // needed for MSVC
class CMAKE_EXPORT CMakeConfig : public QList<CMakeConfigItem>
{
public:
CMakeConfig() = default;
CMakeConfig(const QList<CMakeConfigItem> &items) : QList<CMakeConfigItem>(items) {}
CMakeConfig(std::initializer_list<CMakeConfigItem> items) : QList<CMakeConfigItem>(items) {}
const QList<CMakeConfigItem> &toList() const { return *this; }
static CMakeConfig fromArguments(const QStringList &list);
static CMakeConfig fromFile(const Utils::FilePath &input, QString *errorMessage);
QByteArray valueOf(const QByteArray &key) const;
QString stringValueOf(const QByteArray &key) const;
Utils::FilePath filePathValueOf(const QByteArray &key) const;
QString expandedValueOf(const ProjectExplorer::Kit *k, const QByteArray &key) const;
};
} // namespace CMakeProjectManager

View File

@@ -996,15 +996,15 @@ void CMakeConfigurationKitAspect::setConfiguration(Kit *k, const CMakeConfig &co
{
if (!k)
return;
const QStringList tmp = Utils::transform(config, [](const CMakeConfigItem &i) { return i.toString(); });
const QStringList tmp = Utils::transform(config.toList(),
[](const CMakeConfigItem &i) { return i.toString(); });
k->setValue(CONFIGURATION_ID, tmp);
}
QStringList CMakeConfigurationKitAspect::toStringList(const Kit *k)
{
QStringList current
= Utils::transform(CMakeConfigurationKitAspect::configuration(k),
[](const CMakeConfigItem &i) { return i.toString(); });
QStringList current = Utils::transform(CMakeConfigurationKitAspect::configuration(k).toList(),
[](const CMakeConfigItem &i) { return i.toString(); });
current = Utils::filtered(current, [](const QString &s) { return !s.isEmpty(); });
Utils::sort(current);
return current;
@@ -1023,7 +1023,7 @@ void CMakeConfigurationKitAspect::fromStringList(Kit *k, const QStringList &in)
QStringList CMakeConfigurationKitAspect::toArgumentsList(const Kit *k)
{
return Utils::transform(CMakeConfigurationKitAspect::configuration(k),
return Utils::transform(CMakeConfigurationKitAspect::configuration(k).toList(),
[](const CMakeConfigItem &i) { return i.toArgument(nullptr); });
}
@@ -1046,8 +1046,8 @@ QVariant CMakeConfigurationKitAspect::defaultValue(const Kit *k) const
{
// FIXME: Convert preload scripts
CMakeConfig config = defaultConfiguration(k);
const QStringList tmp
= Utils::transform(config, [](const CMakeConfigItem &i) { return i.toString(); });
const QStringList tmp = Utils::transform(config.toList(),
[](const CMakeConfigItem &i) { return i.toString(); });
return tmp;
}

View File

@@ -135,20 +135,16 @@ QStringList CMakeProjectImporter::importCandidates()
static FilePath qmakeFromCMakeCache(const CMakeConfig &config)
{
// Qt4 way to define things (more convenient for us, so try this first;-)
FilePath qmake
= FilePath::fromUtf8(CMakeConfigItem::valueOf(QByteArray("QT_QMAKE_EXECUTABLE"), config));
const FilePath qmake = config.filePathValueOf("QT_QMAKE_EXECUTABLE");
qCDebug(cmInputLog) << "QT_QMAKE_EXECUTABLE=" << qmake.toUserOutput();
if (!qmake.isEmpty())
return qmake;
// Check Qt5 settings: oh, the horror!
const FilePath qtCMakeDir = [config] {
FilePath tmp = FilePath::fromUtf8(
CMakeConfigItem::valueOf(QByteArray("Qt5Core_DIR"), config));
if (tmp.isEmpty()) {
tmp = FilePath::fromUtf8(
CMakeConfigItem::valueOf(QByteArray("Qt6Core_DIR"), config));
}
FilePath tmp = config.filePathValueOf("Qt5Core_DIR");
if (tmp.isEmpty())
tmp = config.filePathValueOf("Qt6Core_DIR");
return tmp;
}();
qCDebug(cmInputLog) << "QtXCore_DIR=" << qtCMakeDir.toUserOutput();
@@ -206,11 +202,11 @@ static FilePath qmakeFromCMakeCache(const CMakeConfig &config)
cmake.setEnvironment(env);
cmake.setTimeOutMessageBoxEnabled(false);
QString cmakeGenerator = CMakeConfigItem::stringValueOf(QByteArray("CMAKE_GENERATOR"), config);
FilePath cmakeExecutable = CMakeConfigItem::filePathValueOf(QByteArray("CMAKE_COMMAND"), config);
FilePath cmakeMakeProgram =CMakeConfigItem::filePathValueOf(QByteArray("CMAKE_MAKE_PROGRAM"), config);
FilePath toolchainFile = CMakeConfigItem::filePathValueOf(QByteArray("CMAKE_TOOLCHAIN_FILE"), config);
FilePath hostPath = CMakeConfigItem::filePathValueOf(QByteArray("QT_HOST_PATH"), config);
QString cmakeGenerator = config.stringValueOf(QByteArray("CMAKE_GENERATOR"));
FilePath cmakeExecutable = config.filePathValueOf(QByteArray("CMAKE_COMMAND"));
FilePath cmakeMakeProgram = config.filePathValueOf(QByteArray("CMAKE_MAKE_PROGRAM"));
FilePath toolchainFile = config.filePathValueOf(QByteArray("CMAKE_TOOLCHAIN_FILE"));
FilePath hostPath = config.filePathValueOf(QByteArray("QT_HOST_PATH"));
QStringList args;
args.push_back("-S");
@@ -270,7 +266,7 @@ static QVector<ToolChainDescription> extractToolChainsFromCache(const CMakeConfi
}
if (!haveCCxxCompiler) {
const QByteArray generator = CMakeConfigItem::valueOf(QByteArray("CMAKE_GENERATOR"), config);
const QByteArray generator = config.valueOf("CMAKE_GENERATOR");
QString cCompilerName;
QString cxxCompilerName;
if (generator.contains("Visual Studio")) {
@@ -282,8 +278,7 @@ static QVector<ToolChainDescription> extractToolChainsFromCache(const CMakeConfi
}
if (!cCompilerName.isEmpty() && !cxxCompilerName.isEmpty()) {
const FilePath linker = FilePath::fromUtf8(
CMakeConfigItem::valueOf(QByteArray("CMAKE_LINKER"), config));
const FilePath linker = config.filePathValueOf("CMAKE_LINKER");
if (!linker.isEmpty()) {
const FilePath compilerPath = linker.parentDir();
result.append({compilerPath.pathAppended(cCompilerName),
@@ -315,13 +310,11 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
return { };
}
QByteArrayList buildConfigurationTypes = {CMakeConfigItem::valueOf("CMAKE_BUILD_TYPE", config)};
QByteArrayList buildConfigurationTypes = {config.valueOf("CMAKE_BUILD_TYPE")};
if (buildConfigurationTypes.front().isEmpty()) {
QByteArray buildConfigurationTypesString =
CMakeConfigItem::valueOf("CMAKE_CONFIGURATION_TYPES", config);
if (!buildConfigurationTypesString.isEmpty()) {
QByteArray buildConfigurationTypesString = config.valueOf("CMAKE_CONFIGURATION_TYPES");
if (!buildConfigurationTypesString.isEmpty())
buildConfigurationTypes = buildConfigurationTypesString.split(';');
}
}
QList<void *> result;
@@ -329,7 +322,7 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
auto data = std::make_unique<DirectoryData>();
data->cmakeHomeDirectory =
FilePath::fromUserInput(CMakeConfigItem::stringValueOf("CMAKE_HOME_DIRECTORY", config))
FilePath::fromUserInput(config.stringValueOf("CMAKE_HOME_DIRECTORY"))
.canonicalPath();
const FilePath canonicalProjectDirectory = projectDirectory().canonicalPath();
if (data->cmakeHomeDirectory != canonicalProjectDirectory) {
@@ -344,12 +337,12 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
data->buildDirectory = importPath;
data->cmakeBuildType = buildType;
data->cmakeBinary = CMakeConfigItem::filePathValueOf("CMAKE_COMMAND", config);
data->generator = CMakeConfigItem::stringValueOf("CMAKE_GENERATOR", config);
data->extraGenerator = CMakeConfigItem::stringValueOf("CMAKE_EXTRA_GENERATOR", config);
data->platform = CMakeConfigItem::stringValueOf("CMAKE_GENERATOR_PLATFORM", config);
data->toolset = CMakeConfigItem::stringValueOf("CMAKE_GENERATOR_TOOLSET", config);
data->sysroot = CMakeConfigItem::filePathValueOf("CMAKE_SYSROOT", config);
data->cmakeBinary = config.filePathValueOf("CMAKE_COMMAND");
data->generator = config.stringValueOf("CMAKE_GENERATOR");
data->extraGenerator = config.stringValueOf("CMAKE_EXTRA_GENERATOR");
data->platform = config.stringValueOf("CMAKE_GENERATOR_PLATFORM");
data->toolset = config.stringValueOf("CMAKE_GENERATOR_TOOLSET");
data->sysroot = config.filePathValueOf("CMAKE_SYSROOT");
// Qt:
const FilePath qmake = qmakeFromCMakeCache(config);

View File

@@ -203,7 +203,7 @@ QList<ConfigModel::DataItem> ConfigModel::configurationForCMake() const
void ConfigModel::setConfiguration(const CMakeConfig &config)
{
setConfiguration(Utils::transform(config, [](const CMakeConfigItem &i) {
setConfiguration(Utils::transform(config.toList(), [](const CMakeConfigItem &i) {
return DataItem(i);
}));
}

View File

@@ -337,7 +337,7 @@ void FileApiReader::writeConfigurationIntoBuildDirectory(const QStringList &conf
QByteArray contents;
contents.append("# This file is managed by Qt Creator, do not edit!\n\n");
contents.append(
transform(CMakeConfigItem::itemsFromArguments(configurationArguments),
transform(CMakeConfig::fromArguments(configurationArguments).toList(),
[](const CMakeConfigItem &item) {
return item.toCMakeSetLine(nullptr);
})

View File

@@ -69,6 +69,8 @@
#include <QHeaderView>
#include <QLoggingCategory>
#include <QPushButton>
#include <QRandomGenerator>
#include <QRegularExpression>
#include <QTextBrowser>
#include <QToolButton>
#include <QThread>
@@ -304,9 +306,11 @@ public:
});
}
~DockerDevicePrivate() { delete m_shell; }
~DockerDevicePrivate() { stopCurrentContainer(); }
bool runInContainer(const CommandLine &cmd) const;
bool runInShell(const CommandLine &cmd) const;
QString outputForRunInShell(const CommandLine &cmd) const;
void tryCreateLocalFileAccess();
@@ -318,6 +322,7 @@ public:
// For local file access
QPointer<QtcProcess> m_shell;
mutable QMutex m_shellMutex;
QString m_container;
QString m_mergedDir;
QFileSystemWatcher m_mergedDirWatcher;
@@ -359,12 +364,15 @@ public:
auto daemonStateLabel = new QLabel(tr("Daemon state:"));
m_daemonReset = new QToolButton;
m_daemonReset->setIcon(Icons::INFO.icon());
m_daemonReset->setToolTip(tr("Daemon state not evaluated."));
m_daemonReset->setToolTip(tr("Clear detected daemon state. "
"It will be automatically re-evaluated next time an access is needed."));
m_daemonState = new QLabel(tr("Daemon state not evaluated."));
connect(m_daemonReset, &QToolButton::clicked, this, [this, dockerDevice] {
dockerDevice->resetDaemonState();
m_daemonReset->setIcon(Icons::INFO.icon());
m_daemonReset->setToolTip(tr("Daemon state not evaluated."));
m_daemonState->setText(tr("Daemon state not evaluated."));
});
m_runAsOutsideUser = new QCheckBox(tr("Run as outside user"));
@@ -403,10 +411,11 @@ public:
if (!dockerDevice->isDaemonRunning()) {
logView->append(tr("Docker daemon appears to be not running."));
m_daemonReset->setToolTip(tr("Daemon not running. Push to reset the state."));
m_daemonState->setText(tr("Docker daemon not running."));
m_daemonReset->setIcon(Icons::CRITICAL.icon());
} else {
m_daemonReset->setToolTip(tr("Docker daemon running."));
logView->append(tr("Docker daemon appears to be running."));
m_daemonState->setText(tr("Docker daemon running."));
m_daemonReset->setIcon(Icons::OK.icon());
}
@@ -427,7 +436,7 @@ public:
Form {
idLabel, m_idLineEdit, Break(),
repoLabel, m_repoLineEdit, Break(),
daemonStateLabel, m_daemonReset, Break(),
daemonStateLabel, m_daemonReset, m_daemonState, Break(),
m_runAsOutsideUser, Break(),
tr("Paths to mount:"), m_pathsLineEdit, Break(),
Column {
@@ -445,6 +454,7 @@ private:
QLineEdit *m_idLineEdit;
QLineEdit *m_repoLineEdit;
QToolButton *m_daemonReset;
QLabel *m_daemonState;
QCheckBox *m_runAsOutsideUser;
QLineEdit *m_pathsLineEdit;
@@ -745,6 +755,20 @@ void DockerDevicePrivate::stopCurrentContainer()
if (m_container.isEmpty() || m_accessible == NoDaemon)
return;
if (m_shell) {
QMutexLocker l(&m_shellMutex);
m_shell->write("exit\n");
m_shell->waitForFinished(2000);
if (m_shell->state() == QProcess::NotRunning) {
LOG("Clean exit via shell");
m_container.clear();
m_mergedDir.clear();
delete m_shell;
m_shell = nullptr;
return;
}
}
QtcProcess proc;
proc.setCommand({"docker", {"container", "stop", m_container}});
@@ -1019,7 +1043,7 @@ bool DockerDevice::isExecutableFile(const FilePath &filePath) const
return res;
}
const QString path = filePath.path();
return d->runInContainer({"test", {"-x", path}});
return d->runInShell({"test", {"-x", path}});
}
bool DockerDevice::isReadableFile(const FilePath &filePath) const
@@ -1033,7 +1057,7 @@ bool DockerDevice::isReadableFile(const FilePath &filePath) const
return res;
}
const QString path = filePath.path();
return d->runInContainer({"test", {"-r", path, "-a", "-f", path}});
return d->runInShell({"test", {"-r", path, "-a", "-f", path}});
}
bool DockerDevice::isWritableFile(const Utils::FilePath &filePath) const
@@ -1047,7 +1071,7 @@ bool DockerDevice::isWritableFile(const Utils::FilePath &filePath) const
return res;
}
const QString path = filePath.path();
return d->runInContainer({"test", {"-w", path, "-a", "-f", path}});
return d->runInShell({"test", {"-w", path, "-a", "-f", path}});
}
bool DockerDevice::isReadableDirectory(const FilePath &filePath) const
@@ -1061,7 +1085,7 @@ bool DockerDevice::isReadableDirectory(const FilePath &filePath) const
return res;
}
const QString path = filePath.path();
return d->runInContainer({"test", {"-r", path, "-a", "-d", path}});
return d->runInShell({"test", {"-r", path, "-a", "-d", path}});
}
bool DockerDevice::isWritableDirectory(const FilePath &filePath) const
@@ -1075,7 +1099,7 @@ bool DockerDevice::isWritableDirectory(const FilePath &filePath) const
return res;
}
const QString path = filePath.path();
return d->runInContainer({"test", {"-w", path, "-a", "-d", path}});
return d->runInShell({"test", {"-w", path, "-a", "-d", path}});
}
bool DockerDevice::isFile(const FilePath &filePath) const
@@ -1089,7 +1113,7 @@ bool DockerDevice::isFile(const FilePath &filePath) const
return res;
}
const QString path = filePath.path();
return d->runInContainer({"test", {"-f", path}});
return d->runInShell({"test", {"-f", path}});
}
bool DockerDevice::isDirectory(const FilePath &filePath) const
@@ -1103,7 +1127,7 @@ bool DockerDevice::isDirectory(const FilePath &filePath) const
return res;
}
const QString path = filePath.path();
return d->runInContainer({"test", {"-d", path}});
return d->runInShell({"test", {"-d", path}});
}
bool DockerDevice::createDirectory(const FilePath &filePath) const
@@ -1131,7 +1155,7 @@ bool DockerDevice::exists(const FilePath &filePath) const
return res;
}
const QString path = filePath.path();
return d->runInContainer({"test", {"-e", path}});
return d->runInShell({"test", {"-e", path}});
}
bool DockerDevice::ensureExistingFile(const FilePath &filePath) const
@@ -1145,7 +1169,7 @@ bool DockerDevice::ensureExistingFile(const FilePath &filePath) const
return res;
}
const QString path = filePath.path();
return d->runInContainer({"touch", {path}});
return d->runInShell({"touch", {path}});
}
bool DockerDevice::removeFile(const FilePath &filePath) const
@@ -1172,9 +1196,15 @@ bool DockerDevice::removeRecursively(const FilePath &filePath) const
LOG("Remove recursively? " << filePath.toUserOutput() << localAccess.toUserOutput() << res);
return res;
}
// Open this up only when really needed.
// return d->runInContainer({"rm", "-rf", {filePath.path()}});
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->runInContainer({"rm", {"-rf", "--", path}});
}
bool DockerDevice::copyFile(const FilePath &filePath, const FilePath &target) const
@@ -1240,8 +1270,44 @@ FilePath DockerDevice::symLinkTarget(const FilePath &filePath) const
return {};
return mapToGlobalPath(target);
}
QTC_CHECK(false);
return {};
const QString output = d->outputForRunInShell({"readlink", {"-n", "-e", filePath.path()}});
return output.isEmpty() ? FilePath() : filePath.withNewPath(output);
}
static FilePaths filterEntriesHelper(const FilePath &base,
const QStringList &entries,
const QStringList &nameFilters,
QDir::Filters filters,
QDir::SortFlags sort)
{
const QList<QRegularExpression> nameRegexps = transform(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 false;
};
// FIXME: Handle sort and filters. For now bark on unsupported options.
QTC_CHECK(filters == QDir::NoFilter);
QTC_CHECK(sort == QDir::NoSort);
FilePaths result;
for (const QString &entry : entries) {
if (!nameMatches(entry))
continue;
result.append(base.pathAppended(entry));
}
return result;
}
FilePaths DockerDevice::directoryEntries(const FilePath &filePath,
@@ -1258,13 +1324,9 @@ FilePaths DockerDevice::directoryEntries(const FilePath &filePath,
});
}
QtcProcess proc;
proc.setCommand({"ls", {"-1", "-b", "--", filePath.path()}});
runProcess(proc);
proc.waitForFinished();
QStringList entries = proc.stdOut().split('\n', Qt::SkipEmptyParts);
return FilePath::filterEntriesHelper(filePath, entries, nameFilters, filters, sort);
const QString output = d->outputForRunInShell({"ls", {"-1", "-b", "--", filePath.path()}});
QStringList entries = output.split('\n', Qt::SkipEmptyParts);
return filterEntriesHelper(filePath, entries, nameFilters, filters, sort);
}
QByteArray DockerDevice::fileContents(const FilePath &filePath, qint64 limit, qint64 offset) const
@@ -1291,15 +1353,35 @@ QByteArray DockerDevice::fileContents(const FilePath &filePath, qint64 limit, qi
return output;
}
bool DockerDevice::writeFileContents(const Utils::FilePath &filePath, const QByteArray &data) const
bool DockerDevice::writeFileContents(const FilePath &filePath, const QByteArray &data) const
{
QTC_ASSERT(handlesFile(filePath), return {});
tryCreateLocalFileAccess();
if (hasLocalFileAccess())
return mapToLocalAccess(filePath).writeFileContents(data);
QTC_CHECK(false); // FIXME: Implement
return {};
// This following would be the generic Unix solution.
// But it doesn't pass input. FIXME: Why?
// QtcProcess proc;
// proc.setCommand({"dd", {"of=" + filePath.path()}});
// proc.setWriteData(data);
// runProcess(proc);
// proc.waitForFinished();
TemporaryFile tempFile("dockertransport-XXXXXX");
tempFile.open();
tempFile.write(data);
const QString tempName = tempFile.fileName();
tempFile.close();
CommandLine cmd{"docker", {"cp", tempName, d->m_container + ':' + filePath.path()}};
QtcProcess proc;
proc.setCommand(cmd);
proc.runBlocking();
return proc.exitCode() == 0;
}
void DockerDevice::runProcess(QtcProcess &process) const
@@ -1375,6 +1457,48 @@ bool DockerDevicePrivate::runInContainer(const CommandLine &cmd) const
return exitCode == 0;
}
bool DockerDevicePrivate::runInShell(const CommandLine &cmd) const
{
if (m_accessible == NoDaemon)
return false;
QTC_ASSERT(m_shell, return false);
QMutexLocker l(&m_shellMutex);
m_shell->readAllStandardOutput(); // clean possible left-overs
m_shell->write(cmd.toUserOutput().toUtf8() + "\necho $?\n");
m_shell->waitForReadyRead();
QByteArray output = m_shell->readAllStandardOutput();
int result = output.toInt();
LOG("Run command in shell:" << cmd.toUserOutput() << "result: " << output << " ==>" << result);
return result == 0;
}
// generate hex value
static QByteArray randomHex()
{
quint32 val = QRandomGenerator::global()->generate();
return QString::number(val, 16).toUtf8();
}
QString DockerDevicePrivate::outputForRunInShell(const CommandLine &cmd) const
{
if (m_accessible == NoDaemon)
return {};
QTC_ASSERT(m_shell, return {});
QMutexLocker l(&m_shellMutex);
m_shell->readAllStandardOutput(); // clean possible left-overs
const QByteArray markerWithNewLine("___QC_DOCKER_" + randomHex() + "_OUTPUT_MARKER___\n");
m_shell->write(cmd.toUserOutput().toUtf8() + "\necho -n \"" + markerWithNewLine + "\"\n");
QByteArray output;
while (!output.endsWith(markerWithNewLine)) {
m_shell->waitForReadyRead();
output.append(m_shell->readAllStandardOutput());
}
LOG("Run command in shell:" << cmd.toUserOutput() << "output size:" << output.size());
if (QTC_GUARD(output.endsWith(markerWithNewLine)))
output.chop(markerWithNewLine.size());
return QString::fromUtf8(output);
}
// Factory
DockerDeviceFactory::DockerDeviceFactory()

View File

@@ -525,7 +525,7 @@ DeviceManager::DeviceManager(bool isInstance) : d(std::make_unique<DeviceManager
return device->systemEnvironment();
};
FilePath::setDeviceFileHooks(deviceHooks);
FileUtils::setDeviceFileHooks(deviceHooks);
DeviceProcessHooks processHooks;

View File

@@ -347,7 +347,7 @@ void ItemLibraryModel::update(ItemLibraryInfo *itemLibraryInfo, Model *model)
importSection = importHash[entry.requiredImport()];
}
} else if (catName == "My Quick3D Components") {
} else if (catName == ItemLibraryImport::quick3DAssetsTitle()) {
importSection = importHash[ItemLibraryImport::quick3DAssetsTitle()];
} else {
if (catName.startsWith("Qt Quick - "))

View File

@@ -79,6 +79,7 @@ extend_qtc_executable(sdktool
DEFINES QTCREATOR_UTILS_STATIC_LIB
SOURCES
environment.cpp environment.h
filepath.cpp filepath.h
fileutils.cpp fileutils.h
hostosinfo.cpp hostosinfo.h
namevaluedictionary.cpp namevaluedictionary.h

View File

@@ -32,6 +32,7 @@ SOURCES += \
rmtoolchainoperation.cpp \
settings.cpp \
$$UTILS/environment.cpp \
$$UTILS/filepath.cpp \
$$UTILS/fileutils.cpp \
$$UTILS/hostosinfo.cpp \
$$UTILS/namevaluedictionary.cpp \
@@ -65,6 +66,7 @@ HEADERS += \
rmtoolchainoperation.h \
settings.h \
$$UTILS/environment.h \
$$UTILS/filepath.h \
$$UTILS/fileutils.h \
$$UTILS/hostosinfo.h \
$$UTILS/namevaluedictionary.h \

View File

@@ -70,6 +70,7 @@ QtcTool {
files: [
"commandline.cpp", "commandline.h",
"environment.cpp", "environment.h",
"filepath.cpp", "filepath.h",
"fileutils.cpp", "fileutils.h",
"hostosinfo.cpp", "hostosinfo.h",
"namevaluedictionary.cpp", "namevaluedictionary.h",

View File

@@ -19,6 +19,7 @@ HEADERS += \
SOURCES += \
$$UTILSDIR/commandline.cpp \
$$UTILSDIR/environment.cpp \
$$UTILSDIR/filepath.cpp \
$$UTILSDIR/fileutils.cpp \
$$UTILSDIR/hostosinfo.cpp \
$$UTILSDIR/launcherinterface.cpp \
@@ -34,6 +35,7 @@ HEADERS += \
HEADERS += \
$$UTILSDIR/commandline.h \
$$UTILSDIR/environment.h \
$$UTILSDIR/filepath.h \
$$UTILSDIR/fileutils.h \
$$UTILSDIR/hostosinfo.h \
$$UTILSDIR/launcherinterface.h \