forked from qt-creator/qt-creator
Merge "Merge remote-tracking branch 'origin/9.0'"
This commit is contained in:
@@ -220,22 +220,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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -45,8 +45,8 @@ RangeMover {
|
||||
onRangeLeftChanged: updateZoomer()
|
||||
|
||||
Connections {
|
||||
target: zoomer
|
||||
function onWindowChanged() { updateRange(); }
|
||||
target: selectionRange.zoomer
|
||||
function onWindowChanged() { selectionRange.updateRange(); }
|
||||
}
|
||||
|
||||
function setPos(pos) {
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -11,4 +11,3 @@ Text {
|
||||
renderType: Text.NativeRendering
|
||||
color: Theme.color(Theme.Timeline_TextColor)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
#include <utils/icon.h>
|
||||
|
||||
#include <QGraphicsOpacityEffect>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QGuiApplication>
|
||||
#include <QPaintEvent>
|
||||
#include <QPainter>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QStyleOption>
|
||||
|
||||
#include <qdrawutil.h>
|
||||
@@ -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
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "utils_global.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QToolButton>
|
||||
|
||||
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;
|
||||
|
||||
975
src/libs/utils/devicefileaccess.cpp
Normal file
975
src/libs/utils/devicefileaccess.cpp
Normal file
@@ -0,0 +1,975 @@
|
||||
// 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 "commandline.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QOperatingSystemVersion>
|
||||
#include <QRegularExpression>
|
||||
#include <QStorageInfo>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#ifdef QTCREATOR_PCH_H
|
||||
#define CALLBACK WINAPI
|
||||
#endif
|
||||
#include <qt_windows.h>
|
||||
#include <shlobj.h>
|
||||
#else
|
||||
#include <qplatformdefs.h>
|
||||
#endif
|
||||
|
||||
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::isSymLink(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<QByteArray> 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;
|
||||
}
|
||||
|
||||
QByteArray DeviceFileAccess::fileId(const FilePath &filePath) const
|
||||
{
|
||||
Q_UNUSED(filePath);
|
||||
QTC_CHECK(false);
|
||||
return {};
|
||||
}
|
||||
|
||||
void DeviceFileAccess::asyncFileContents(
|
||||
const FilePath &filePath,
|
||||
const Continuation<std::optional<QByteArray>> &cont,
|
||||
qint64 limit,
|
||||
qint64 offset) const
|
||||
{
|
||||
cont(fileContents(filePath, limit, offset));
|
||||
}
|
||||
|
||||
void DeviceFileAccess::asyncWriteFileContents(
|
||||
const FilePath &filePath,
|
||||
const Continuation<bool> &cont,
|
||||
const QByteArray &data,
|
||||
qint64 offset) const
|
||||
{
|
||||
cont(writeFileContents(filePath, data, offset));
|
||||
}
|
||||
|
||||
void DeviceFileAccess::asyncCopyFile(
|
||||
const FilePath &filePath,
|
||||
const Continuation<bool> &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::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());
|
||||
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<QByteArray> 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();
|
||||
}
|
||||
|
||||
// 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<FILE_INFO_BY_HANDLE_CLASS>(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<const char *>(&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);
|
||||
return HostOsInfo::hostOs();
|
||||
}
|
||||
|
||||
|
||||
// UnixDeviceAccess
|
||||
|
||||
UnixDeviceFileAccess::~UnixDeviceFileAccess() = default;
|
||||
|
||||
bool UnixDeviceFileAccess::runInShellSuccess(const CommandLine &cmdLine,
|
||||
const QByteArray &stdInData) const
|
||||
{
|
||||
return runInShell(cmdLine, stdInData).exitCode == 0;
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::isExecutableFile(const FilePath &filePath) const
|
||||
{
|
||||
const QString path = filePath.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}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::isWritableFile(const FilePath &filePath) const
|
||||
{
|
||||
const QString path = filePath.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}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::isWritableDirectory(const FilePath &filePath) const
|
||||
{
|
||||
const QString path = filePath.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}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::isDirectory(const FilePath &filePath) const
|
||||
{
|
||||
const QString path = filePath.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}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::ensureExistingFile(const FilePath &filePath) const
|
||||
{
|
||||
const QString path = filePath.path();
|
||||
return runInShellSuccess({"touch", {path}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::createDirectory(const FilePath &filePath) const
|
||||
{
|
||||
const QString path = filePath.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}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::removeFile(const FilePath &filePath) const
|
||||
{
|
||||
return runInShellSuccess({"rm", {filePath.path()}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
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}, OsType::OsTypeLinux});
|
||||
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()}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::renameFile(const FilePath &filePath, const FilePath &target) const
|
||||
{
|
||||
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()}, OsType::OsTypeLinux});
|
||||
const QString out = QString::fromUtf8(result.stdOut);
|
||||
return out.isEmpty() ? FilePath() : filePath.withNewPath(out);
|
||||
}
|
||||
|
||||
std::optional<QByteArray> 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, OsType::OsTypeLinux});
|
||||
|
||||
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, OsType::OsTypeLinux}, 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()}, OsType::OsTypeLinux});
|
||||
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()}, OsType::OsTypeLinux});
|
||||
const uint bits = result.stdOut.toUInt(nullptr, 8);
|
||||
QFileDevice::Permissions perm = {};
|
||||
#define BIT(n, p) if (bits & (1<<n)) perm |= QFileDevice::p
|
||||
BIT(0, ExeOther);
|
||||
BIT(1, WriteOther);
|
||||
BIT(2, ReadOther);
|
||||
BIT(3, ExeGroup);
|
||||
BIT(4, WriteGroup);
|
||||
BIT(5, ReadGroup);
|
||||
BIT(6, ExeUser);
|
||||
BIT(7, WriteUser);
|
||||
BIT(8, ReadUser);
|
||||
#undef BIT
|
||||
return perm;
|
||||
}
|
||||
|
||||
bool UnixDeviceFileAccess::setPermissions(const FilePath &filePath, QFile::Permissions perms) const
|
||||
{
|
||||
const int flags = int(perms);
|
||||
return runInShellSuccess({"chmod", {QString::number(flags, 16), filePath.path()}, OsType::OsTypeLinux});
|
||||
}
|
||||
|
||||
qint64 UnixDeviceFileAccess::fileSize(const FilePath &filePath) const
|
||||
{
|
||||
const RunResult result = runInShell({"stat", {"-L", "-c", "%s", filePath.path()}, OsType::OsTypeLinux});
|
||||
return result.stdOut.toLongLong();
|
||||
}
|
||||
|
||||
qint64 UnixDeviceFileAccess::bytesAvailable(const FilePath &filePath) const
|
||||
{
|
||||
const RunResult result = runInShell({"df", {"-k", filePath.path()}, OsType::OsTypeLinux});
|
||||
return FileUtils::bytesAvailableFromDFOutput(result.stdOut);
|
||||
}
|
||||
|
||||
QByteArray UnixDeviceFileAccess::fileId(const FilePath &filePath) const
|
||||
{
|
||||
const RunResult result = runInShell({"stat", {"-L", "-c", "%D:%i", filePath.path()}, OsType::OsTypeLinux});
|
||||
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()}, OsType::OsTypeLinux});
|
||||
return FileUtils::filePathInfoFromTriple(QString::fromLatin1(stat.stdOut));
|
||||
}
|
||||
|
||||
// returns whether 'find' could be used.
|
||||
bool UnixDeviceFileAccess::iterateWithFind(
|
||||
const FilePath &filePath,
|
||||
const FileFilter &filter,
|
||||
const FilePath::IterateDirCallback &callBack) const
|
||||
{
|
||||
QTC_CHECK(filePath.isAbsolutePath());
|
||||
|
||||
CommandLine cmdLine{"find", filter.asFindArguments(filePath.path()), OsType::OsTypeLinux};
|
||||
|
||||
// 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.
|
||||
if (callBack.index() == 1)
|
||||
cmdLine.addArgs(R"(-exec echo -n \"{}\"" " \; -exec stat -L -c "%f %Y %s" "{}" \;)", 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;
|
||||
|
||||
const auto toFilePath = [&filePath, &callBack](const QString &entry) -> 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}, OsType::OsTypeLinux});
|
||||
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<QRegularExpression> 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
|
||||
195
src/libs/utils/devicefileaccess.h
Normal file
195
src/libs/utils/devicefileaccess.h
Normal file
@@ -0,0 +1,195 @@
|
||||
// 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 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;
|
||||
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 QByteArray fileId(const FilePath &filePath) const;
|
||||
|
||||
virtual void iterateDirectory(
|
||||
const FilePath &filePath,
|
||||
const FilePath::IterateDirCallback &callBack,
|
||||
const FileFilter &filter) const;
|
||||
|
||||
virtual std::optional<QByteArray> 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<std::optional<QByteArray>> &cont,
|
||||
qint64 limit,
|
||||
qint64 offset) const;
|
||||
|
||||
virtual void asyncWriteFileContents(
|
||||
const FilePath &filePath,
|
||||
const Continuation<bool> &cont,
|
||||
const QByteArray &data,
|
||||
qint64 offset) const;
|
||||
|
||||
virtual void asyncCopyFile(
|
||||
const FilePath &filePath,
|
||||
const Continuation<bool> &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 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;
|
||||
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;
|
||||
QByteArray fileId(const FilePath &filePath) const override;
|
||||
|
||||
void iterateDirectory(
|
||||
const FilePath &filePath,
|
||||
const FilePath::IterateDirCallback &callBack,
|
||||
const FileFilter &filter) const override;
|
||||
|
||||
std::optional<QByteArray> 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 CommandLine &cmdLine,
|
||||
const QByteArray &inputData = {}) const = 0;
|
||||
bool runInShellSuccess(const CommandLine &cmdLine, 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 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;
|
||||
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;
|
||||
QByteArray fileId(const FilePath &filePath) const override;
|
||||
|
||||
void iterateDirectory(
|
||||
const FilePath &filePath,
|
||||
const FilePath::IterateDirCallback &callBack,
|
||||
const FileFilter &filter) const override;
|
||||
|
||||
std::optional<QByteArray> 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
|
||||
@@ -14,142 +14,24 @@ Q_LOGGING_CATEGORY(deviceShellLog, "qtc.utils.deviceshell", QtWarningMsg)
|
||||
|
||||
namespace Utils {
|
||||
|
||||
namespace {
|
||||
|
||||
/*!
|
||||
* The multiplex script waits for input via stdin.
|
||||
*
|
||||
* To start a command, a message is send with the format "<cmd-id> "<base64-encoded-stdin-data>" <commandline>\n"
|
||||
* To start a command, a message is send with
|
||||
* the format "<cmd-id> "<base64-encoded-stdin-data>" <commandline>\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".
|
||||
*
|
||||
*/
|
||||
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)
|
||||
@@ -380,8 +262,11 @@ bool DeviceShell::installShellScript()
|
||||
return false;
|
||||
}
|
||||
|
||||
const static QByteArray shellScriptBase64
|
||||
= QByteArray(r_execScript.begin(), r_execScript.size()).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";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <QDirIterator>
|
||||
#include <QFileInfo>
|
||||
#include <QRegularExpression>
|
||||
#include <QStorageInfo>
|
||||
#include <QUrl>
|
||||
#include <QStringView>
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
namespace Utils {
|
||||
|
||||
static DeviceFileHooks s_deviceHooks;
|
||||
static bool removeRecursivelyLocal(const FilePath &filePath, QString *error);
|
||||
inline bool isWindowsDriveLetter(QChar ch);
|
||||
|
||||
|
||||
@@ -147,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)
|
||||
@@ -359,136 +357,73 @@ 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::isSymLink() const
|
||||
{
|
||||
return fileAccess()->isSymLink(*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,34 +450,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()) {
|
||||
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()))
|
||||
return;
|
||||
}
|
||||
fileAccess()->iterateDirectory(*this, callBack, filter);
|
||||
}
|
||||
|
||||
void FilePath::iterateDirectories(const FilePaths &dirs,
|
||||
@@ -555,26 +463,7 @@ void FilePath::iterateDirectories(const FilePaths &dirs,
|
||||
|
||||
std::optional<QByteArray> 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
|
||||
@@ -588,78 +477,30 @@ bool FilePath::ensureReachable(const FilePath &other) const
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void FilePath::asyncFileContents(const Continuation<const std::optional<QByteArray> &> &cont,
|
||||
qint64 maxSize,
|
||||
qint64 offset) const
|
||||
void FilePath::asyncFileContents(
|
||||
const Continuation<const std::optional<QByteArray> &> &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<bool> &cont,
|
||||
const QByteArray &data,
|
||||
qint64 offset) const
|
||||
void FilePath::asyncWriteFileContents(
|
||||
const Continuation<bool> &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
|
||||
@@ -678,26 +519,34 @@ 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
|
||||
{
|
||||
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
|
||||
@@ -948,6 +797,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.
|
||||
@@ -1041,6 +907,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()))
|
||||
@@ -1112,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();
|
||||
@@ -1252,10 +1118,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);
|
||||
}
|
||||
|
||||
@@ -1263,6 +1129,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)
|
||||
@@ -1270,7 +1137,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
|
||||
@@ -1349,47 +1217,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);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1401,11 +1249,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
|
||||
@@ -1417,11 +1261,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<void(bool)> &cont, const FilePath &target) const
|
||||
@@ -1431,90 +1271,24 @@ void FilePath::asyncCopyFile(const std::function<void(bool)> &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);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -1949,19 +1723,15 @@ 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;
|
||||
bool isFirst = true;
|
||||
filterOptions << "(";
|
||||
for (const QString ¤t : nameFilters) {
|
||||
if (current.indexOf(oneChar) != -1) {
|
||||
qDebug() << "Skipped" << current << "due to presence of [] wildcard";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (addedFirst)
|
||||
if (!isFirst)
|
||||
filterOptions << "-o";
|
||||
filterOptions << nameOption << current;
|
||||
addedFirst = true;
|
||||
isFirst = false;
|
||||
}
|
||||
filterOptions << ")";
|
||||
}
|
||||
arguments << filterOptions;
|
||||
return arguments;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QDateTime;
|
||||
@@ -27,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 <class ...Args> using Continuation = std::function<void(Args...)>;
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT FileFilter
|
||||
{
|
||||
public:
|
||||
@@ -103,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;
|
||||
@@ -154,17 +159,20 @@ public:
|
||||
[[nodiscard]] FilePath onDevice(const FilePath &deviceTemplate) const;
|
||||
[[nodiscard]] FilePath withNewPath(const QString &newPath) const;
|
||||
|
||||
using IterateDirCallback = std::function<bool(const FilePath &item)>;
|
||||
using IterateDirWithInfoCallback
|
||||
= std::function<bool(const FilePath &item, const FilePathInfo &info)>;
|
||||
using IterateDirCallback
|
||||
= std::variant<
|
||||
std::function<bool(const FilePath &item)>,
|
||||
std::function<bool(const FilePath &item, const FilePathInfo &info)>
|
||||
>;
|
||||
|
||||
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 = {},
|
||||
@@ -184,7 +192,6 @@ public:
|
||||
static void sort(FilePaths &files);
|
||||
|
||||
// Asynchronous interface
|
||||
template <class ...Args> using Continuation = std::function<void(Args...)>;
|
||||
void asyncCopyFile(const Continuation<bool> &cont, const FilePath &target) const;
|
||||
void asyncFileContents(const Continuation<const std::optional<QByteArray> &> &cont,
|
||||
qint64 maxSize = -1,
|
||||
@@ -201,6 +208,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);
|
||||
@@ -228,6 +236,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;
|
||||
@@ -248,54 +257,11 @@ class QTCREATOR_UTILS_EXPORT DeviceFileHooks
|
||||
public:
|
||||
static DeviceFileHooks &instance();
|
||||
|
||||
std::function<bool(const FilePath &)> isExecutableFile;
|
||||
std::function<bool(const FilePath &)> isReadableFile;
|
||||
std::function<bool(const FilePath &)> isReadableDir;
|
||||
std::function<bool(const FilePath &)> isWritableDir;
|
||||
std::function<bool(const FilePath &)> isWritableFile;
|
||||
std::function<bool(const FilePath &)> isFile;
|
||||
std::function<bool(const FilePath &)> isDir;
|
||||
std::function<bool(const FilePath &)> ensureWritableDir;
|
||||
std::function<bool(const FilePath &)> ensureExistingFile;
|
||||
std::function<bool(const FilePath &)> createDir;
|
||||
std::function<bool(const FilePath &)> exists;
|
||||
std::function<bool(const FilePath &)> removeFile;
|
||||
std::function<bool(const FilePath &)> removeRecursively;
|
||||
std::function<bool(const FilePath &, const FilePath &)> copyFile;
|
||||
std::function<bool(const FilePath &, const FilePath &)> renameFile;
|
||||
std::function<FilePath(const FilePath &, const FilePaths &)> searchInPath;
|
||||
std::function<FilePath(const FilePath &)> symLinkTarget;
|
||||
std::function<QString(const FilePath &)> mapToDevicePath;
|
||||
std::function<void(const FilePath &,
|
||||
const FilePath::IterateDirCallback &, // Abort on 'false' return.
|
||||
const FileFilter &)>
|
||||
iterateDirectory;
|
||||
std::function<void(const FilePath &,
|
||||
const FilePath::IterateDirWithInfoCallback &, // Abort on 'false' return.
|
||||
const FileFilter &)>
|
||||
iterateDirectoryWithInfo;
|
||||
std::function<std::optional<QByteArray>(const FilePath &, qint64, qint64)> fileContents;
|
||||
std::function<bool(const FilePath &, const QByteArray &, qint64)> writeFileContents;
|
||||
std::function<QDateTime(const FilePath &)> lastModified;
|
||||
std::function<QFile::Permissions(const FilePath &)> permissions;
|
||||
std::function<bool(const FilePath &, QFile::Permissions)> setPermissions;
|
||||
std::function<OsType(const FilePath &)> osType;
|
||||
std::function<Environment(const FilePath &)> environment;
|
||||
std::function<qint64(const FilePath &)> fileSize;
|
||||
std::function<qint64(const FilePath &)> bytesAvailable;
|
||||
std::function<DeviceFileAccess *(const FilePath &)> fileAccess;
|
||||
std::function<QString(const FilePath &)> deviceDisplayName;
|
||||
std::function<bool(const FilePath &, const FilePath &)> isSameDevice;
|
||||
std::function<FilePathInfo(const FilePath &)> filePathInfo;
|
||||
|
||||
|
||||
template <class ...Args> using Continuation = std::function<void(Args...)>;
|
||||
std::function<void(const Continuation<bool> &, const FilePath &, const FilePath &)> asyncCopyFile;
|
||||
std::function<void(
|
||||
const Continuation<const std::optional<QByteArray> &> &, const FilePath &, qint64, qint64)>
|
||||
asyncFileContents;
|
||||
std::function<void(const Continuation<bool> &, const FilePath &, const QByteArray &, qint64)>
|
||||
asyncWriteFileContents;
|
||||
std::function<bool(const FilePath &, const FilePath &)> ensureReachable;
|
||||
std::function<Environment(const FilePath &)> environment;
|
||||
std::function<bool(const FilePath &left, const FilePath &right)> isSameDevice;
|
||||
};
|
||||
|
||||
} // namespace Utils
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "savefile.h"
|
||||
|
||||
#include "algorithm.h"
|
||||
#include "commandline.h"
|
||||
#include "qtcassert.h"
|
||||
#include "hostosinfo.h"
|
||||
|
||||
@@ -15,7 +14,6 @@
|
||||
#include <QDataStream>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QOperatingSystemVersion>
|
||||
#include <QRegularExpression>
|
||||
#include <QTemporaryFile>
|
||||
#include <QTextStream>
|
||||
@@ -25,7 +23,6 @@
|
||||
|
||||
#ifdef QT_GUI_LIB
|
||||
#include <QMessageBox>
|
||||
#include <QRegularExpression>
|
||||
#include <QGuiApplication>
|
||||
#endif
|
||||
|
||||
@@ -37,7 +34,7 @@
|
||||
#include <shlobj.h>
|
||||
#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<FILE_INFO_BY_HANDLE_CLASS>(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<const char *>(&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<void()> &task)
|
||||
@@ -476,6 +405,82 @@ 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()) {
|
||||
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);
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
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 +489,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,25 +516,46 @@ 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,
|
||||
const QString &caption,
|
||||
const FilePath &dir,
|
||||
QFileDialog::Options options)
|
||||
QFileDialog::Options options,
|
||||
bool fromDeviceIfShiftIsPressed)
|
||||
{
|
||||
const QString result = QFileDialog::getExistingDirectory(dialogParent(parent),
|
||||
caption,
|
||||
dir.toString(),
|
||||
options);
|
||||
return FilePath::fromString(result);
|
||||
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,
|
||||
dir,
|
||||
{},
|
||||
nullptr,
|
||||
options,
|
||||
schemes,
|
||||
forceNonNativeDialog,
|
||||
QFileDialog::Directory,
|
||||
QFileDialog::AcceptOpen));
|
||||
}
|
||||
|
||||
FilePaths FileUtils::getOpenFilePaths(QWidget *parent,
|
||||
@@ -534,92 +565,23 @@ 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<QUrl> 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
|
||||
|
||||
// Used on 'ls' output on unix-like systems.
|
||||
void FileUtils::iterateLsOutput(const FilePath &base,
|
||||
const QStringList &entries,
|
||||
const FileFilter &filter,
|
||||
const std::function<bool (const FilePath &)> &callBack)
|
||||
{
|
||||
const QList<QRegularExpression> 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;
|
||||
if (!callBack(base.pathAppended(entry)))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FilePathInfo::FileFlags fileInfoFlagsfromStatRawModeHex(const QString &hexString)
|
||||
{
|
||||
bool ok = false;
|
||||
@@ -675,148 +637,6 @@ FilePathInfo FileUtils::filePathInfoFromTriple(const QString &infos)
|
||||
return {size, flags, dt};
|
||||
}
|
||||
|
||||
bool iterateWithFind(const FilePath &filePath,
|
||||
const FileFilter &filter,
|
||||
const std::function<RunResult(const CommandLine &)> &runInShell,
|
||||
const std::function<bool(const QString &)> 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);
|
||||
// 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();
|
||||
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<RunResult(const CommandLine &)> &runInShell,
|
||||
const FilePath::IterateDirCallback &callBack)
|
||||
{
|
||||
const auto toFilePath = [&filePath, &callBack](const QString &entry){
|
||||
return 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<RunResult(const CommandLine &)> &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);
|
||||
|
||||
const FilePathInfo fi = FileUtils::filePathInfoFromTriple(infos);
|
||||
if (!fi.fileFlags)
|
||||
return true;
|
||||
|
||||
const FilePath fp = filePath.withNewPath(fileName);
|
||||
return callBack(fp, fi);
|
||||
};
|
||||
|
||||
return iterateWithFind(filePath, filter, runInShell, toFilePathAndInfo, infoArgs);
|
||||
}
|
||||
|
||||
static void findUsingLs(const QString ¤t,
|
||||
const FileFilter &filter,
|
||||
const std::function<RunResult(const CommandLine &)> &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<RunResult (const CommandLine &)> &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) {
|
||||
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);
|
||||
}
|
||||
|
||||
void FileUtils::iterateUnixDirectory(const FilePath &filePath,
|
||||
const FileFilter &filter,
|
||||
bool *useFind,
|
||||
const std::function<RunResult(const CommandLine &)> &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());
|
||||
});
|
||||
}
|
||||
|
||||
/*!
|
||||
Copies the directory specified by \a srcFilePath recursively to \a tgtFilePath. \a tgtFilePath will contain
|
||||
the target directory, which will be created. Example usage:
|
||||
|
||||
@@ -81,31 +81,10 @@ 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);
|
||||
|
||||
static void iterateLsOutput(
|
||||
const FilePath &base,
|
||||
const QStringList &entries,
|
||||
const FileFilter &filter,
|
||||
const std::function<bool(const FilePath &)> &callBack);
|
||||
|
||||
static void iterateUnixDirectory(
|
||||
const FilePath &base,
|
||||
const FileFilter &filter,
|
||||
bool *useFind,
|
||||
const std::function<RunResult(const CommandLine &)> &runInShell,
|
||||
const FilePath::IterateDirCallback &callBack);
|
||||
|
||||
static void iterateUnixDirectory(
|
||||
const FilePath &base,
|
||||
const FileFilter &filter,
|
||||
bool *useFind,
|
||||
const std::function<RunResult(const CommandLine &)> &runInShell,
|
||||
const FilePath::IterateDirWithInfoCallback &callBack);
|
||||
|
||||
static qint64 bytesAvailableFromDFOutput(const QByteArray &dfOutput);
|
||||
|
||||
static FilePathInfo filePathInfoFromTriple(const QString &infos);
|
||||
@@ -131,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,
|
||||
@@ -139,13 +119,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
|
||||
|
||||
};
|
||||
|
||||
@@ -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
|
||||
@@ -60,6 +66,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<FilePath, CachedData> m_cache;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
116
src/libs/utils/scripts/deviceshell.sh
Normal file
116
src/libs/utils/scripts/deviceshell.sh
Normal file
@@ -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
|
||||
@@ -83,6 +83,8 @@ Project {
|
||||
"detailsbutton.h",
|
||||
"detailswidget.cpp",
|
||||
"detailswidget.h",
|
||||
"devicefileaccess.cpp",
|
||||
"devicefileaccess.h",
|
||||
"deviceshell.cpp",
|
||||
"deviceshell.h",
|
||||
"differ.cpp",
|
||||
|
||||
@@ -239,6 +239,7 @@
|
||||
<file>images/message@2x.png</file>
|
||||
<file>images/help.png</file>
|
||||
<file alias="mimetypes/freedesktop.org.xml" compression-algorithm="best">../3rdparty/xdg/freedesktop.org.xml</file>
|
||||
<file>scripts/deviceshell.sh</file>
|
||||
</qresource>
|
||||
<qresource prefix="/codemodel">
|
||||
<file>images/enum.png</file>
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "qdbconstants.h"
|
||||
|
||||
#include <projectexplorer/deploymentdataview.h>
|
||||
#include "projectexplorer/devicesupport/idevice.h"
|
||||
#include <projectexplorer/kitinformation.h>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/target.h>
|
||||
@@ -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
|
||||
|
||||
@@ -92,7 +92,8 @@ public:
|
||||
QdbMakeDefaultAppStep(BuildStepList *bsl, Id id)
|
||||
: AbstractRemoteLinuxDeployStep(bsl, id)
|
||||
{
|
||||
auto service = createDeployService<QdbMakeDefaultAppService>();
|
||||
auto service = new QdbMakeDefaultAppService;
|
||||
setDeployService(service);
|
||||
|
||||
auto selection = addAspect<SelectionAspect>();
|
||||
selection->setSettingsKey("QdbMakeDefaultDeployStep.MakeDefault");
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <remotelinux/genericdirectuploadstep.h>
|
||||
#include <remotelinux/makeinstallstep.h>
|
||||
#include <remotelinux/rsyncdeploystep.h>
|
||||
#include <remotelinux/remotelinux_constants.h>
|
||||
|
||||
#include <utils/hostosinfo.h>
|
||||
@@ -156,6 +157,8 @@ public:
|
||||
|
||||
QdbDeployStepFactory<RemoteLinux::GenericDirectUploadStep>
|
||||
m_directUploadStepFactory{RemoteLinux::Constants::DirectUploadStepId};
|
||||
QdbDeployStepFactory<RemoteLinux::RsyncDeployStep>
|
||||
m_rsyncDeployStepFactory{RemoteLinux::Constants::RsyncDeployStepId};
|
||||
QdbDeployStepFactory<RemoteLinux::MakeInstallStep>
|
||||
m_makeInstallStepFactory{RemoteLinux::Constants::MakeInstallStepId};
|
||||
|
||||
|
||||
@@ -115,7 +115,8 @@ public:
|
||||
QdbStopApplicationStep(BuildStepList *bsl, Id id)
|
||||
: AbstractRemoteLinuxDeployStep(bsl, id)
|
||||
{
|
||||
auto service = createDeployService<QdbStopApplicationService>();
|
||||
auto service = new QdbStopApplicationService;
|
||||
setDeployService(service);
|
||||
|
||||
setWidgetExpandedByDefault(false);
|
||||
|
||||
|
||||
@@ -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<QString> &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<QString> &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);
|
||||
@@ -732,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)
|
||||
@@ -741,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();
|
||||
|
||||
@@ -44,12 +44,25 @@ public:
|
||||
ClangdAstNode ast;
|
||||
};
|
||||
|
||||
class ReplacementData {
|
||||
public:
|
||||
QString oldSymbolName;
|
||||
QString newSymbolName;
|
||||
QSet<Utils::FilePath> fileRenameCandidates;
|
||||
};
|
||||
|
||||
class ClangdFindReferences::Private
|
||||
{
|
||||
public:
|
||||
Private(ClangdFindReferences *q) : q(q) {}
|
||||
|
||||
ClangdClient *client() const { return qobject_cast<ClangdClient *>(q->parent()); }
|
||||
static void handleRenameRequest(
|
||||
const SearchResult *search,
|
||||
const ReplacementData &replacementData,
|
||||
const QString &newSymbolName,
|
||||
const QList<SearchResultItem> &checkedItems,
|
||||
bool preserveCase);
|
||||
void handleFindUsagesResult(const QList<Location> &locations);
|
||||
void finishSearch();
|
||||
void reportAllSearchResultsAndFinish();
|
||||
@@ -61,24 +74,50 @@ public:
|
||||
QMap<DocumentUri, ReferencesFileData> fileData;
|
||||
QList<MessageId> pendingAstRequests;
|
||||
QPointer<SearchResult> search;
|
||||
std::optional<ReplacementData> 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<QString> &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<SearchResultItem> &checkedItems,
|
||||
bool preserveCase) {
|
||||
const auto replacementData = search->userData().value<ReplacementData>();
|
||||
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<SearchResultItem> &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<QCheckBox *>(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<Location> &locations)
|
||||
{
|
||||
if (!search || canceled) {
|
||||
@@ -154,7 +218,7 @@ void ClangdFindReferences::Private::handleFindUsagesResult(const QList<Location>
|
||||
}
|
||||
|
||||
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<QCheckBox *>(
|
||||
search->additionalReplaceWidget());
|
||||
QTC_CHECK(renameCheckBox);
|
||||
const QSet<Utils::FilePath> files = replacementData->fileRenameCandidates;
|
||||
renameCheckBox->setText(tr("Re&name %n files", nullptr, files.size()));
|
||||
const QStringList filesForUser = Utils::transform<QStringList>(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)
|
||||
|
||||
@@ -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<QString> &replacement, bool categorize);
|
||||
~ClangdFindReferences();
|
||||
|
||||
signals:
|
||||
|
||||
@@ -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) {
|
||||
@@ -155,7 +154,7 @@ GenerateCompilationDbResult generateCompilationDB(QList<ProjectInfo::ConstPtr> 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(),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -175,14 +175,10 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildSystem *bs) :
|
||||
BuildConfiguration *bc = bs->buildConfiguration();
|
||||
CMakeBuildConfiguration *cbc = static_cast<CMakeBuildConfiguration *>(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,
|
||||
}
|
||||
}
|
||||
}.setSpacing(0)
|
||||
},
|
||||
clearBox,
|
||||
envWidget
|
||||
}
|
||||
}.attachTo(details, WithoutMargins);
|
||||
|
||||
Column {
|
||||
m_configureDetailsWidget,
|
||||
}.attachTo(this, WithoutMargins);
|
||||
|
||||
updateAdvancedCheckBox();
|
||||
setError(m_buildSystem->error());
|
||||
setWarning(m_buildSystem->warning());
|
||||
|
||||
@@ -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();
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}();
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <coreplugin/editormanager/ieditor.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/messagemanager.h>
|
||||
#include <cppeditor/cpptoolsreuse.h>
|
||||
#include <projectexplorer/buildmanager.h>
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
#include <projectexplorer/projectexplorerconstants.h>
|
||||
@@ -217,10 +218,17 @@ void CMakeManager::buildFile(Node *node)
|
||||
CMakeTargetNode *targetNode = dynamic_cast<CMakeTargetNode *>(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();
|
||||
|
||||
@@ -264,18 +264,18 @@ QList<CMakeBuildTarget> 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<CMakeBuildTarget> 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);
|
||||
}
|
||||
|
||||
@@ -5,34 +5,42 @@
|
||||
#include "actionmanager.h"
|
||||
#include "command.h"
|
||||
|
||||
|
||||
#include <utils/proxyaction.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
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;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <utils/id.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QPointer>
|
||||
#include <QString>
|
||||
#include <QToolButton>
|
||||
@@ -15,10 +16,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<Command> 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 +51,4 @@ private:
|
||||
QPointer<Command> m_command;
|
||||
QString m_toolTipBase;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <utils/filepath.h>
|
||||
#include <utils/id.h>
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QObject>
|
||||
#include <QPair>
|
||||
|
||||
@@ -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 = {},
|
||||
|
||||
@@ -3249,11 +3249,11 @@ void EditorManager::addCloseEditorListener(const std::function<bool (IEditor *)>
|
||||
|
||||
\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)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "utils/link.h"
|
||||
#include "utils/textfileformat.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QList>
|
||||
#include <QWidget>
|
||||
|
||||
@@ -90,7 +91,7 @@ public:
|
||||
static bool openExternalEditor(const Utils::FilePath &filePath, Utils::Id editorId);
|
||||
static void addCloseEditorListener(const std::function<bool(IEditor *)> &listener);
|
||||
|
||||
static Utils::FilePaths getOpenFilePaths();
|
||||
static Utils::FilePaths getOpenFilePaths(QFileDialog::Options options = {});
|
||||
|
||||
static IDocument *currentDocument();
|
||||
static IEditor *currentEditor();
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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<QUrl> 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -34,8 +34,6 @@ private:
|
||||
QNetworkReply *m_listReply = nullptr;
|
||||
|
||||
QString m_fetchId;
|
||||
int m_postId = -1;
|
||||
bool m_hostChecked = false;
|
||||
};
|
||||
|
||||
} // CodePaster
|
||||
|
||||
@@ -25,15 +25,19 @@ PasteSelectDialog::PasteSelectDialog(const QList<Protocol*> &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);
|
||||
|
||||
@@ -41,10 +41,12 @@ PasteView::PasteView(const QList<Protocol *> &protocols,
|
||||
m_commentPlaceHolder(Tr::tr("<Comment>")),
|
||||
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<Protocol *> &protocols,
|
||||
m_uiUsername->setPlaceholderText(Tr::tr("<Username>"));
|
||||
|
||||
m_uiDescription = new QLineEdit(this);
|
||||
m_uiDescription->setObjectName("uiDescription");
|
||||
m_uiDescription->setPlaceholderText(Tr::tr("<Description>"));
|
||||
|
||||
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
@@ -83,6 +86,7 @@ PasteView::PasteView(const QList<Protocol *> &protocols,
|
||||
groupBox->setFlat(true);
|
||||
|
||||
m_plainTextEdit = new QPlainTextEdit;
|
||||
m_plainTextEdit->setObjectName("plainTextEdit");
|
||||
|
||||
m_stackedWidget = new QStackedWidget(this);
|
||||
m_stackedWidget->addWidget(groupBox);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 &) {
|
||||
|
||||
@@ -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)},
|
||||
|
||||
@@ -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<ProjectInfo::ConstPtr> &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";
|
||||
|
||||
@@ -32,7 +32,7 @@ struct Utils
|
||||
static QString toString(const QVector<ProjectFile> &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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -28,11 +28,6 @@ namespace CppEditor {
|
||||
class CPPEDITOR_EXPORT ProjectPart
|
||||
{
|
||||
public:
|
||||
enum ToolChainWordWidth {
|
||||
WordWidth32Bit,
|
||||
WordWidth64Bit,
|
||||
};
|
||||
|
||||
using ConstPtr = QSharedPointer<const ProjectPart>;
|
||||
|
||||
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;
|
||||
|
||||
@@ -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<QAction> m_loadJson;
|
||||
|
||||
@@ -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)
|
||||
@@ -771,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;
|
||||
@@ -787,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();
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,8 @@ private:
|
||||
|
||||
void createFullBacktrace();
|
||||
|
||||
void handleDoInterruptInferior(const QString &errorMessage);
|
||||
|
||||
typedef QPair<QString, QString> SourcePathMapping;
|
||||
struct NormalizedSourceFileName // Struct for caching mapped/normalized source files.
|
||||
{
|
||||
@@ -129,7 +131,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);
|
||||
@@ -176,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<int, DebuggerCommand> m_commandForToken;
|
||||
QString m_currentBuiltinResponse;
|
||||
|
||||
@@ -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<FileData>());
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/basetreeview.h>
|
||||
#include <utils/devicefileaccess.h>
|
||||
#include <utils/deviceshell.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/fileutils.h>
|
||||
@@ -113,6 +114,21 @@ private:
|
||||
FilePath m_devicePath;
|
||||
};
|
||||
|
||||
class DockerDeviceFileAccess : public UnixDeviceFileAccess
|
||||
{
|
||||
public:
|
||||
DockerDeviceFileAccess(DockerDevicePrivate *dev)
|
||||
: m_dev(dev)
|
||||
{}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
class DockerDevicePrivate : public QObject
|
||||
{
|
||||
public:
|
||||
@@ -125,11 +141,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;
|
||||
}
|
||||
|
||||
std::optional<QByteArray> fileContents(const FilePath &filePath, qint64 limit, qint64 offset);
|
||||
|
||||
void updateContainerAccess();
|
||||
void changeMounts(QStringList newMounts);
|
||||
@@ -177,8 +188,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 +348,36 @@ Tasks DockerDevicePrivate::validateMounts() const
|
||||
return result;
|
||||
}
|
||||
|
||||
RunResult DockerDeviceFileAccess::runInShell(const CommandLine &cmdLine,
|
||||
const QByteArray &stdInData) const
|
||||
{
|
||||
QTC_ASSERT(m_dev, return {});
|
||||
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;
|
||||
}
|
||||
|
||||
OsType DockerDeviceFileAccess::osType(const FilePath &filePath) const
|
||||
{
|
||||
QTC_ASSERT(m_dev, return UnixDeviceFileAccess::osType(filePath));
|
||||
return m_dev->q->osType();
|
||||
}
|
||||
|
||||
DockerDevice::DockerDevice(DockerSettings *settings, const DockerDeviceData &data)
|
||||
: d(new DockerDevicePrivate(this, settings, data))
|
||||
{
|
||||
setFileAccess(&d->m_fileAccess);
|
||||
setDisplayType(Tr::tr("Docker"));
|
||||
setOsType(OsTypeOtherUnix);
|
||||
setDefaultDisplayName(Tr::tr("Docker Image"));
|
||||
@@ -423,7 +461,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;
|
||||
}
|
||||
|
||||
@@ -448,20 +486,6 @@ void DockerDevicePrivate::stopCurrentContainer()
|
||||
m_cachedEnviroment.clear();
|
||||
}
|
||||
|
||||
static QString getLocalIPv4Address()
|
||||
{
|
||||
const QList<QHostAddress> 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);
|
||||
@@ -483,18 +507,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)
|
||||
@@ -505,8 +532,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
|
||||
@@ -530,7 +567,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",
|
||||
@@ -744,19 +781,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"/");
|
||||
@@ -778,167 +802,11 @@ 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<<n)) perm |= QFileDevice::p
|
||||
BIT(0, ExeOther);
|
||||
BIT(1, WriteOther);
|
||||
BIT(2, ReadOther);
|
||||
BIT(3, ExeGroup);
|
||||
BIT(4, WriteGroup);
|
||||
BIT(5, ReadGroup);
|
||||
BIT(6, ExeUser);
|
||||
BIT(7, WriteUser);
|
||||
BIT(8, ReadUser);
|
||||
#undef BIT
|
||||
return perm;
|
||||
}
|
||||
|
||||
bool DockerDevice::setPermissions(const FilePath &filePath,
|
||||
QFileDevice::Permissions permissions) const
|
||||
{
|
||||
Q_UNUSED(permissions)
|
||||
QTC_ASSERT(handlesFile(filePath), return {});
|
||||
QTC_CHECK(false); // FIXME: Implement.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DockerDevice::ensureReachable(const FilePath &other) const
|
||||
{
|
||||
if (other.isSameDevice(rootPath()))
|
||||
return true;
|
||||
|
||||
if (other.needsDevice())
|
||||
return false;
|
||||
|
||||
@@ -947,52 +815,6 @@ bool DockerDevice::ensureReachable(const FilePath &other) const
|
||||
return d->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);
|
||||
}
|
||||
|
||||
void DockerDevice::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);
|
||||
}
|
||||
|
||||
std::optional<QByteArray> 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();
|
||||
@@ -1004,28 +826,6 @@ void DockerDevice::aboutToBeRemoved() const
|
||||
detector.undoAutoDetect(id().toString());
|
||||
}
|
||||
|
||||
std::optional<QByteArray> 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();
|
||||
@@ -1277,8 +1077,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;
|
||||
}
|
||||
|
||||
@@ -79,44 +79,10 @@ 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;
|
||||
|
||||
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;
|
||||
void iterateDirectory(const Utils::FilePath &filePath,
|
||||
const Utils::FilePath::IterateDirWithInfoCallback &callBack,
|
||||
const Utils::FileFilter &filter) const override;
|
||||
std::optional<QByteArray> 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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1353,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;
|
||||
|
||||
@@ -28,8 +28,9 @@
|
||||
#include <QImageReader>
|
||||
#include <QLabel>
|
||||
#include <QMap>
|
||||
#include <QMenu>
|
||||
#include <QSpacerItem>
|
||||
#include <QWidget>
|
||||
#include <QToolBar>
|
||||
#include <QWidget>
|
||||
|
||||
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,127 @@ 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);
|
||||
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);
|
||||
auto setAsDefault = new QAction(Tr::tr("Set as Default"), d->toolbar);
|
||||
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(
|
||||
"<p>"
|
||||
+ Tr::tr("Use the current settings for background, outline, and fitting "
|
||||
"to screen as the default for new image viewers. Current default:")
|
||||
+ "</p><p><ul><li>" + Tr::tr("Background: %1").arg(settings.showBackground ? on : off)
|
||||
+ "</li><li>" + Tr::tr("Outline: %1").arg(settings.showOutline ? on : off) + "</li><li>"
|
||||
+ Tr::tr("Fit to Screen: %1").arg(settings.fitToScreen ? on : off) + "</li></ul>");
|
||||
};
|
||||
updateSetAsDefaultToolTip();
|
||||
|
||||
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,
|
||||
@@ -243,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();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -280,18 +275,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 +305,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 +355,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<TextEditor::TextDocument *> &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();
|
||||
}
|
||||
|
||||
@@ -198,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)
|
||||
@@ -218,17 +219,17 @@ void LanguageClientManager::deleteClient(Client *client)
|
||||
for (QList<Client *> &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()
|
||||
@@ -408,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;
|
||||
|
||||
@@ -100,7 +100,6 @@ private:
|
||||
|
||||
QList<Client *> reachableClients();
|
||||
|
||||
bool m_shuttingDown = false;
|
||||
QList<Client *> m_clients;
|
||||
QList<BaseSettings *> m_currentSettings; // owned
|
||||
QMap<QString, QList<Client *>> m_clientsForSetting;
|
||||
|
||||
@@ -38,15 +38,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)
|
||||
@@ -101,7 +99,7 @@ FilePath McuPackage::basePath() const
|
||||
|
||||
FilePath McuPackage::path() const
|
||||
{
|
||||
return (basePath() / m_relativePathModifier.path()).cleanPath();
|
||||
return basePath().cleanPath();
|
||||
}
|
||||
|
||||
FilePath McuPackage::defaultPath() const
|
||||
@@ -230,7 +228,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, [&] {
|
||||
@@ -239,10 +237,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] {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -87,7 +86,6 @@ private:
|
||||
QScopedPointer<const McuPackageVersionDetector> 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;
|
||||
|
||||
@@ -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());
|
||||
@@ -251,15 +251,13 @@ 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)) {
|
||||
QWidget *packageWidget = package->widget();
|
||||
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);
|
||||
packageWidget->show();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)};
|
||||
}
|
||||
|
||||
@@ -542,7 +538,7 @@ static McuAbstractTargetFactory::Ptr createFactory(bool isLegacy,
|
||||
{"arm-greenhills",
|
||||
McuPackagePtr{new McuPackage{settingsHandler,
|
||||
{},
|
||||
toolchainFilePrefix / "arm-ghs.cmake",
|
||||
toolchainFilePrefix / "ghs-arm.cmake",
|
||||
{},
|
||||
{},
|
||||
Legacy::Constants::TOOLCHAIN_FILE_CMAKE_VARIABLE,
|
||||
|
||||
@@ -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/"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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/"
|
||||
|
||||
@@ -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<PackageCreator>("creator");
|
||||
QTest::addColumn<QString>("json");
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<QString>("defaultPath");
|
||||
QTest::addColumn<QString>("setting");
|
||||
QTest::addColumn<QString>("cmakeVar");
|
||||
QTest::addColumn<QString>("envVar");
|
||||
QTest::addColumn<QString>("label");
|
||||
QTest::addColumn<QString>("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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -83,7 +83,6 @@ void ModelEditorPlugin::extensionsInitialized()
|
||||
ExtensionSystem::IPlugin::ShutdownFlag ModelEditorPlugin::aboutToShutdown()
|
||||
{
|
||||
d->settingsController.save(Core::ICore::settings());
|
||||
QApplication::clipboard()->clear();
|
||||
return SynchronousShutdown;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<const CustomToolChain *>(&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;
|
||||
|
||||
@@ -83,7 +83,6 @@ private:
|
||||
|
||||
CustomParserSettings customParserSettings() const;
|
||||
|
||||
Utils::FilePath m_compilerCommand;
|
||||
Utils::FilePath m_makeCommand;
|
||||
|
||||
Macros m_predefinedMacros;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user