Merge remote-tracking branch 'origin/4.0'

Change-Id: I80bc3371c6d7cf9f2d46cbcf8e79e4761213d8aa
This commit is contained in:
Eike Ziller
2016-05-27 12:01:36 +02:00
70 changed files with 1243 additions and 339 deletions

53
dist/changes-4.0.1.md vendored Normal file
View File

@@ -0,0 +1,53 @@
Qt Creator version 4.0.1 contains bug fixes.
The most important changes are listed in this document. For a complete
list of changes, see the Git log for the Qt Creator sources that
you can check out from the public Git repository. For example:
git clone git://code.qt.io/qt-creator/qt-creator.git
git log --cherry-pick --pretty=oneline v4.0.0..v4.0.1
CMake Projects
* Added notification when `CMakeCache.txt` changes and introduces a
conflict with the build configuration settings, with the option
to adapt the build configuration settings
* Fixed that build configurations could not override kit settings, and added
a warning to build configurations that override kit settings
* Fixed that `yes` was not considered as boolean `true` value
* Fixed race between persisting and parsing CMake configuration
(QTCREATORBUG-16258)
QML Support
* Added wizard for Qt Quick Controls 2
* Fixed that `pragma` directives were removed when reformatting
Debugging
* Fixed QObject property expansion (QTCREATORBUG-15798)
* Fixed updating evaluated expressions
* Fixed crash on spontaneous debugger exit (QTCREATORBUG-16233)
Valgrind
* Fixed crash when changing filter
Clang Static Analyzer
* Fixed executing Clang with absolute path on Windows (QTCREATORBUG-16234)
Test Integration
* Fixed running tests on Windows with Qbs (QTCREATORBUG-16323)
Beautifier
* Fixed regression with `clang-format` and predefined style set to `File`
(QTCREATORBUG-16239)
Platform Specific
Windows
* Fixed detection of Microsoft Visual C++ Build Tools

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -120,10 +120,12 @@
\li Qt Quick Controls Application
Create a Qt Quick application using
\l{http://doc-snapshots.qt.io/qt5-5.7/qtquickcontrols2-index.html}
{Qt Quick Controls 2} (requires Qt 5.7 or later),
\l{http://doc.qt.io/qt-5/qtquickcontrols-index.html}
{Qt Quick Controls} or
{Qt Quick Controls}, or
\l{http://doc.qt.io/qt-5/qtlabscontrols-index.html}
{Qt Labs Controls}
{Qt Labs Controls} (only for Qt 5.6)
\li Qt Canvas 3D Application

View File

@@ -50,13 +50,16 @@
a QQuickView. You can build the application and deploy it to desktop,
embedded, and mobile target platforms.
\li \uicontrol {Qt Quick Controls Application} and
\li \uicontrol {Qt Quick Controls Application},
\uicontrol {Qt Quick Controls 2 Application}, and
\uicontrol {Qt Labs Controls Application} are like
\uicontrol {Qt Quick Application}, but using
\l{http://doc-snapshots.qt.io/qt5-5.7/qtquickcontrols2-index.html}
{Qt Quick Controls 2} (requires Qt 5.7 or later),
\l{http://doc.qt.io/qt-5/qtquickcontrols-index.html}
{Qt Quick Controls} or
{Qt Quick Controls}, or
\l{http://doc.qt.io/qt-5/qtlabscontrols-index.html}
{Qt Labs Controls}
{Qt Labs Controls} (only for Qt 5.6)
\li \uicontrol {Qt Canvas 3D Application} creates a Qt Quick application
that imports the Qt Canvas 3D module and, optionally, includes

View File

@@ -188,7 +188,11 @@ class PlainDumper:
def importPlainDumpers(args):
if args == "off":
try:
gdb.execute("disable pretty-printer .* .*")
except:
# Might occur in non-ASCII directories
warn("COULD NOT DISABLE PRETTY PRINTERS")
else:
theDumper.importPlainDumpers()

View File

@@ -2640,7 +2640,10 @@ def qdumpHelper__QJsonValue(d, data, base, pv):
if t == 2:
d.putType("QJsonValue (Number)")
if latinOrIntValue:
d.putValue(v)
w = toInteger(v)
if w >= 0x4000000:
w -= 0x8000000
d.putValue(w)
else:
data = base + v;
d.putValue(d.extractBlob(data, 8).extractDouble())
@@ -2741,10 +2744,12 @@ def qdump__QJsonValue(d, value):
d.putType("QJsonValue (Bool)")
v = toInteger(value["b"])
d.putValue("true" if v else "false")
d.putNumChild(0)
return
if t == 2:
d.putType("QJsonValue (Number)")
d.putValue(value["dbl"])
d.putNumChild(0)
return
if t == 3:
d.putType("QJsonValue (String)")

View File

@@ -43,7 +43,7 @@ QHash<QObject *, DesignerCustomObjectData*> m_objectToDataHash;
DesignerCustomObjectData::DesignerCustomObjectData(QObject *object)
: m_object(object)
{
if (object)
if (object) {
populateResetHashes();
m_objectToDataHash.insert(object, this);
QObject::connect(object, &QObject::destroyed, [=] {
@@ -51,6 +51,7 @@ DesignerCustomObjectData::DesignerCustomObjectData(QObject *object)
delete this;
});
}
}
void DesignerCustomObjectData::registerData(QObject *object)
{

View File

@@ -3,7 +3,7 @@
"supportedProjectTypes": [ "Qt4ProjectManager.Qt4Project" ],
"id": "W.QtLabsControlsApplication",
"category": "F.Application",
"trDescription": "Creates a deployable Qt Quick 2 application using Qt Labs Controls.",
"trDescription": "Creates a deployable Qt Quick 2 application using Qt Labs Controls.<br/><br/><b>Note:</b> Qt Labs Controls are only available with Qt 5.6. Choose <em>Qt Quick Controls 2 Application</em> when targeting Qt 5.7 or later.",
"trDisplayName": "Qt Labs Controls Application",
"trDisplayCategory": "Application",
"icon": "../qtquickapplication/qml_wizard.png",

View File

@@ -3,6 +3,9 @@ import QtQuick.Window %{QtQuickWindowVersion}
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
@if %{IsUiFileInUse}
MainForm {

View File

@@ -0,0 +1,10 @@
import QtQuick %{QtQuickVersion}
Page1Form {
button1.onClicked: {
console.log("Button 1 clicked.");
}
button2.onClicked: {
console.log("Button 2 clicked.");
}
}

View File

@@ -0,0 +1,22 @@
import QtQuick %{QtQuickVersion}
import QtQuick.Controls %{QtQuickControls2Version}
import QtQuick.Layouts %{QtQuickLayoutsVersion}
Item {
property alias button1: button1
property alias button2: button2
RowLayout {
anchors.centerIn: parent
Button {
id: button1
text: qsTr("Press Me 1")
}
Button {
id: button2
text: qsTr("Press Me 2")
}
}
}

View File

@@ -0,0 +1,13 @@
QT += qml quick
CONFIG += c++11
SOURCES += %{MainCppFileName}
RESOURCES += qml.qrc
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Default rules for deployment.
include(deployment.pri)

View File

@@ -0,0 +1,12 @@
%{Cpp:LicenseTemplate}\
%{JS: QtSupport.qtIncludes([], ["QtGui/QGuiApplication", "QtQml/QQmlApplicationEngine"])}
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
return app.exec();
}

View File

@@ -0,0 +1,37 @@
import QtQuick %{QtQuickVersion}
import QtQuick.Controls %{QtQuickControls2Version}
import QtQuick.Layouts %{QtQuickLayoutsVersion}
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
SwipeView {
id: swipeView
anchors.fill: parent
currentIndex: tabBar.currentIndex
Page1 {
}
Page {
Label {
text: qsTr("Second page")
anchors.centerIn: parent
}
}
}
footer: TabBar {
id: tabBar
currentIndex: swipeView.currentIndex
TabButton {
text: qsTr("First")
}
TabButton {
text: qsTr("Second")
}
}
}

View File

@@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>Page1.qml</file>
<file>Page1Form.ui.qml</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,86 @@
{
"version": 1,
"supportedProjectTypes": [ "Qt4ProjectManager.Qt4Project" ],
"id": "V.QtQuickControls2Application",
"category": "F.Application",
"trDescription": "Creates a deployable Qt Quick 2 application using Qt Quick Controls 2.<br/><br/><b>Note:</b> Qt Quick Controls 2 are available with Qt 5.7 and later. Choose <em>Qt Labs Controls Application</em> when targeting Qt 5.6.",
"trDisplayName": "Qt Quick Controls 2 Application",
"trDisplayCategory": "Application",
"icon": "../qtquickapplication/qml_wizard.png",
"featuresRequired": [ "QtSupport.Wizards.FeatureQtQuick.Controls.2" ],
"enabled": "%{JS: [ %{Plugins} ].indexOf('QmakeProjectManager') >= 0}",
"options":
[
{ "key": "ProFileName", "value": "%{JS: Util.fileName('%{ProjectDirectory}/%{ProjectName}', 'pro')}" },
{ "key": "MainCppFileName", "value": "%{JS: 'main.' + Util.preferredSuffix('text/x-c++src')}" },
{ "key": "QtQuickVersion", "value": "2.7" },
{ "key": "QtQuickControls2Version", "value": "2.0" },
{ "key": "QtQuickDialogsVersion", "value": "1.0" },
{ "key": "QtQuickLayoutsVersion", "value": "1.0" }
],
"pages":
[
{
"trDisplayName": "Project Location",
"trShortTitle": "Location",
"typeId": "Project"
},
{
"trDisplayName": "Kit Selection",
"trShortTitle": "Kits",
"typeId": "Kits",
"enabled": "%{IsTopLevelProject}",
"data": { "projectFilePath": "%{ProFileName}" }
},
{
"trDisplayName": "Project Management",
"trShortTitle": "Summary",
"typeId": "Summary"
}
],
"generators":
[
{
"typeId": "File",
"data":
[
{
"source": "app.pro",
"target": "%{ProFileName}",
"openAsProject": true
},
{
"source": "main.cpp",
"target": "%{MainCppFileName}"
},
{
"source": "main.qml.tpl",
"target": "main.qml",
"openInEditor": true
},
{
"source": "Page1.qml.tpl",
"target": "Page1.qml"
},
{
"source": "Page1Form.ui.qml.tpl",
"target": "Page1Form.ui.qml"
},
{
"source": "qml.qrc"
},
{
"source": "../../../../shared/qrcdeployment.pri",
"target": "%{ProjectDirectory}/deployment.pri"
},
{
"source": "../../git.ignore",
"target": "%{ProjectDirectory}/.gitignore",
"condition": "%{JS: !%{IsSubproject} && '%{VersionControl}' === 'G.Git'}"
}
]
}
]
}

View File

@@ -480,6 +480,13 @@ protected:
}
}
virtual bool visit(UiPragma *ast)
{
out("pragma ", ast->pragmaToken);
accept(ast->pragmaType);
return false;
}
virtual bool visit(UiImport *ast)
{
out("import ", ast->importToken);
@@ -1187,6 +1194,12 @@ protected:
return false;
}
virtual bool visit(UiQualifiedPragmaId *ast)
{
out(ast->identifierToken);
return false;
}
virtual bool visit(Elision *ast)
{
for (Elision *it = ast; it; it = it->next) {

View File

@@ -447,8 +447,8 @@ static QStringList splitArgsUnix(const QString &args, bool abortOnMeta,
}
}
for (int i = 0; i < val.length(); i++) {
QChar cc = val.unicode()[i];
if (cc == 9 || cc == 10 || cc == 32) {
const QChar cc = val.unicode()[i];
if (cc.unicode() == 9 || cc.unicode() == 10 || cc.unicode() == 32) {
if (hadWord) {
ret += cret;
cret.clear();

View File

@@ -48,13 +48,6 @@ ToolTip::ToolTip() : m_tip(0), m_widget(0)
{
connect(&m_showTimer, &QTimer::timeout, this, &ToolTip::hideTipImmediately);
connect(&m_hideDelayTimer, &QTimer::timeout, this, &ToolTip::hideTipImmediately);
connect(static_cast<QGuiApplication *>(QGuiApplication::instance()),
&QGuiApplication::applicationStateChanged,
[this](Qt::ApplicationState state) {
if (state != Qt::ApplicationActive)
hideTipImmediately();
}
);
}
ToolTip::~ToolTip()
@@ -290,6 +283,11 @@ void ToolTip::placeTip(const QPoint &pos, QWidget *w)
bool ToolTip::eventFilter(QObject *o, QEvent *event)
{
if (m_tip && event->type() == QEvent::ApplicationStateChange
&& qApp->applicationState() != Qt::ApplicationActive) {
hideTipImmediately();
}
if (!o->isWidgetType())
return false;

View File

@@ -194,9 +194,9 @@ void AutoTestUnitTests::testCodeParserGTest()
if (qgetenv("GOOGLETEST_DIR").isEmpty())
QSKIP("This test needs googletest - set GOOGLETEST_DIR (point to googletest repository)");
QFETCH(QString, projectFilePath);
CppTools::Tests::ProjectOpenerAndCloser projectManager;
CppTools::ProjectInfo projectInfo = projectManager.open(
QString(m_tmpDir->path() + QLatin1String("/simple_gt/simple_gt.pro")), true);
CppTools::ProjectInfo projectInfo = projectManager.open(projectFilePath, true);
QVERIFY(projectInfo.isValid());
QSignalSpy parserSpy(m_model->parser(), SIGNAL(parsingFinished()));
@@ -226,5 +226,14 @@ void AutoTestUnitTests::testCodeParserGTest()
QCOMPARE(m_model->dataTagsCount(), 0);
}
void AutoTestUnitTests::testCodeParserGTest_data()
{
QTest::addColumn<QString>("projectFilePath");
QTest::newRow("simpleGoogletest")
<< QString(m_tmpDir->path() + QLatin1String("/simple_gt/simple_gt.pro"));
QTest::newRow("simpleGoogletestQbs")
<< QString(m_tmpDir->path() + QLatin1String("/simple_gt/simple_gt.qbs"));
}
} // namespace Internal
} // namespace Autotest

View File

@@ -51,6 +51,7 @@ private slots:
void testCodeParserSwitchStartup();
void testCodeParserSwitchStartup_data();
void testCodeParserGTest();
void testCodeParserGTest_data();
private:
TestTreeModel *m_model;

View File

@@ -41,17 +41,24 @@
<file>unit_test/mixed_atp/tests/auto/quickauto2/quickauto2.qbs</file>
<file>unit_test/simple_gt/src/main.cpp</file>
<file>unit_test/simple_gt/src/src.pro</file>
<file>unit_test/simple_gt/src/src.qbs</file>
<file>unit_test/simple_gt/tests/gt1/further.cpp</file>
<file>unit_test/simple_gt/tests/gt1/gt1.pro</file>
<file>unit_test/simple_gt/tests/gt1/gt1.qbs</file>
<file>unit_test/simple_gt/tests/gt1/main.cpp</file>
<file>unit_test/simple_gt/tests/gt2/gt2.pro</file>
<file>unit_test/simple_gt/tests/gt2/gt2.qbs</file>
<file>unit_test/simple_gt/tests/gt2/main.cpp</file>
<file>unit_test/simple_gt/tests/gt2/queuetest.h</file>
<file>unit_test/simple_gt/tests/tests.pro</file>
<file>unit_test/simple_gt/tests/tests.qbs</file>
<file>unit_test/simple_gt/simple_gt.pro</file>
<file>unit_test/simple_gt/simple_gt.qbs</file>
<file>unit_test/simple_gt/tests/gtest_dependency.pri</file>
<file>unit_test/simple_gt/tests/gt3/dummytest.h</file>
<file>unit_test/simple_gt/tests/gt3/gt3.pro</file>
<file>unit_test/simple_gt/tests/gt3/gt3.qbs</file>
<file>unit_test/simple_gt/tests/gt3/main.cpp</file>
<file>unit_test/simple_gt/tests/common/functions.js</file>
</qresource>
</RCC>

View File

@@ -115,7 +115,11 @@ void TestConfiguration::completeTestInformation()
foreach (const BuildTargetInfo &bti, appTargets.list) {
// some project manager store line/column information as well inside ProjectPart
if (bti.isValid() && m_proFile.startsWith(bti.projectFilePath.toString())) {
targetFile = Utils::HostOsInfo::withExecutableSuffix(bti.targetFilePath.toString());
targetFile = bti.targetFilePath.toString();
if (Utils::HostOsInfo::isWindowsHost()
&& !targetFile.toLower().endsWith(QLatin1String(".exe"))) {
targetFile = Utils::HostOsInfo::withExecutableSuffix(targetFile);
}
targetName = bti.targetName;
break;
}

View File

@@ -152,6 +152,10 @@ bool TestAstVisitor::visit(CPlusPlus::CallAST *ast)
bool TestAstVisitor::visit(CPlusPlus::CompoundStatementAST *ast)
{
if (!ast || !ast->symbol) {
m_currentScope = 0;
return false;
}
m_currentScope = ast->symbol->asScope();
return true;
}

View File

@@ -75,7 +75,7 @@ public:
private:
QString m_className;
CPlusPlus::Scope *m_currentScope;
CPlusPlus::Scope *m_currentScope = 0;
CPlusPlus::Document::Ptr m_currentDoc;
};

View File

@@ -0,0 +1,10 @@
import qbs
Project {
name: "Simple GT"
references: [
"src/src.qbs",
"tests/tests.qbs"
]
}

View File

@@ -0,0 +1,10 @@
import qbs
CppApplication {
type: "application"
name: "Dummy application"
Depends { name: "Qt.core" }
files: [ "main.cpp" ]
}

View File

@@ -0,0 +1,49 @@
var FileInfo = loadExtension("qbs.FileInfo")
function getGTestDir(str) {
if (!str) {
if (qbs.hostOS.contains("linux"))
return "/usr/include/gtest";
} else {
return FileInfo.joinPaths(str, "googletest");
}
return "";
}
function getGMockDir(str) {
if (!str) {
if (qbs.hostOS.contains("linux"))
return "/usr/include/gmock";
} else {
return FileInfo.joinPaths(str, "googlemock");
}
return "";
}
function getGTestAll(str) {
var gtest = getGTestDir(str);
if (!gtest)
return [];
return [FileInfo.joinPaths(gtest, "src/gtest-all.cc")];
}
function getGMockAll(str) {
var gmock = getGMockDir(str);
if (!gmock)
return [];
return [FileInfo.joinPaths(gmock, "src/gmock-all.cc")];
}
function getGTestIncludes(str) {
var gtest = getGTestDir(str);
if (!gtest)
return [];
return [gtest, FileInfo.joinPaths(gtest, "include")];
}
function getGMockIncludes(str) {
var mock = getGMockDir(str);
if (!mock)
return [];
return [mock, FileInfo.joinPaths(mock, "include")];
}

View File

@@ -0,0 +1,35 @@
import qbs
import qbs.File
import qbs.FileInfo
import "../common/functions.js" as googleCommon
CppApplication {
type: "application"
name: "googletest1"
property string gtestDir: googleCommon.getGTestDir(project.googletestDir)
property string gmockDir: googleCommon.getGMockDir(project.googletestDir)
condition: {
if (File.exists(gtestDir) && File.exists(gmockDir))
return true;
console.error("Cannot find Google Test - specify environment variable GOOGLETEST_DIR "
+ "or qbs property " + project.name + ".googletestDir" );
return false;
}
cpp.includePaths: [].concat(googleCommon.getGTestIncludes(project.googletestDir))
.concat(googleCommon.getGMockIncludes(project.googletestDir))
cpp.cxxLanguageVersion: "c++11"
cpp.defines: ["GTEST_LANG_CXX11"]
cpp.dynamicLibraries: [ "pthread" ]
files: [
// own stuff
"further.cpp",
"main.cpp",
].concat(googleCommon.getGTestAll(project.googletestDir))
.concat(googleCommon.getGMockAll(project.googletestDir))
}

View File

@@ -0,0 +1,36 @@
import qbs
import qbs.File
import qbs.FileInfo
import "../common/functions.js" as googleCommon
CppApplication {
type: "application"
name: "googletest2"
property string gtestDir: googleCommon.getGTestDir(project.googletestDir)
property string gmockDir: googleCommon.getGMockDir(project.googletestDir)
Depends { name: "Qt.core" }
condition: {
if (File.exists(gtestDir) && File.exists(gmockDir))
return true;
console.error("Cannot find Google Test - specify environment variable GOOGLETEST_DIR "
+ "or qbs property " + project.name + ".googletestDir" );
return false;
}
cpp.includePaths: [].concat(googleCommon.getGTestIncludes(project.googletestDir))
.concat(googleCommon.getGMockIncludes(project.googletestDir))
cpp.cxxLanguageVersion: "c++11"
cpp.defines: ["GTEST_LANG_CXX11"]
files: [
// own stuff
"queuetest.h",
"main.cpp",
].concat(googleCommon.getGTestAll(project.googletestDir))
.concat(googleCommon.getGMockAll(project.googletestDir))
}

View File

@@ -0,0 +1,36 @@
import qbs
import qbs.File
import qbs.FileInfo
import "../common/functions.js" as googleCommon
CppApplication {
type: "application"
name: "googletest3"
property string gtestDir: googleCommon.getGTestDir(project.googletestDir)
property string gmockDir: googleCommon.getGMockDir(project.googletestDir)
Depends { name: "Qt.core" }
condition: {
if (File.exists(gtestDir) && File.exists(gmockDir))
return true;
console.error("Cannot find Google Test - specify environment variable GOOGLETEST_DIR "
+ "or qbs property " + project.name + ".googletestDir" );
return false;
}
cpp.includePaths: [].concat(googleCommon.getGTestIncludes(project.googletestDir))
.concat(googleCommon.getGMockIncludes(project.googletestDir))
cpp.cxxLanguageVersion: "c++11"
cpp.defines: ["GTEST_LANG_CXX11"]
files: [
// own stuff
"dummytest.h",
"main.cpp",
].concat(googleCommon.getGTestAll(project.googletestDir))
.concat(googleCommon.getGMockAll(project.googletestDir))
}

View File

@@ -0,0 +1,14 @@
import qbs
import qbs.Environment
Project {
name: "Tests"
property string googletestDir: Environment.getEnv("GOOGLETEST_DIR")
references: [
"gt1/gt1.qbs",
"gt2/gt2.qbs",
"gt3/gt3.qbs"
]
}

View File

@@ -50,25 +50,29 @@ namespace Internal {
QString clangExecutableFromSettings(Core::Id toolchainType, bool *isValid)
{
QString exeFromSettings = ClangStaticAnalyzerSettings::instance()->clangExecutable();
if (toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) {
if (exeFromSettings.endsWith(QLatin1String(QTC_HOST_EXE_SUFFIX)))
exeFromSettings.chop(int(qstrlen(QTC_HOST_EXE_SUFFIX)));
if (exeFromSettings.endsWith(QLatin1String("clang")))
exeFromSettings.append(QLatin1String("-cl"));
}
return clangExecutable(exeFromSettings, isValid);
}
QString clangExecutable(const QString &fileNameOrPath, bool *isValid)
{
QString executable = fileNameOrPath;
QString executable = ClangStaticAnalyzerSettings::instance()->clangExecutable();
if (executable.isEmpty()) {
*isValid = false;
return executable;
}
if (!QFileInfo(executable).isAbsolute()) {
const QString hostExeSuffix = QLatin1String(QTC_HOST_EXE_SUFFIX);
const Qt::CaseSensitivity caseSensitivity = Utils::HostOsInfo::fileNameCaseSensitivity();
const bool hasSuffix = executable.endsWith(hostExeSuffix, caseSensitivity);
if (toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) {
if (hasSuffix)
executable.chop(hostExeSuffix.length());
executable.append(QLatin1String("-cl"));
if (hasSuffix)
executable.append(hostExeSuffix);
}
const QFileInfo fileInfo = QFileInfo(executable);
if (fileInfo.isAbsolute()) {
if (!hasSuffix)
executable.append(hostExeSuffix);
} else {
const Utils::Environment &environment = Utils::Environment::systemEnvironment();
const QString executableFromPath = environment.searchInPath(executable).toString();
if (executableFromPath.isEmpty()) {

View File

@@ -40,7 +40,6 @@ namespace Internal {
bool isClangExecutableUsable(const QString &filePath, QString *errorMessage = 0);
QString clangExecutable(const QString &fileNameOrPath, bool *isValid);
QString clangExecutableFromSettings(Core::Id toolchainType, bool *isValid);
QString createFullLocationString(const Debugger::DiagnosticLocation &location);

View File

@@ -30,6 +30,7 @@
#include "cmakeprojectmanager.h"
#include "cmaketool.h"
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <projectexplorer/kit.h>
@@ -49,6 +50,7 @@
#include <QFile>
#include <QFileInfo>
#include <QFileSystemWatcher>
#include <QMessageBox>
#include <QRegularExpression>
#include <QSet>
#include <QTemporaryDir>
@@ -61,20 +63,37 @@ namespace CMakeProjectManager {
namespace Internal {
static QStringList toArguments(const CMakeConfig &config, const ProjectExplorer::Kit *k) {
const QStringList argList
= Utils::transform(config, [k](const CMakeConfigItem &i) -> QString {
const QString tmp = i.toString();
return tmp.isEmpty() ? QString()
: QString::fromLatin1("-D") + k->macroExpander()->expand(tmp);
return Utils::transform(config, [k](const CMakeConfigItem &i) -> QString {
QString a = QString::fromLatin1("-D");
a.append(QString::fromUtf8(i.key));
switch (i.type) {
case CMakeConfigItem::FILEPATH:
a.append(QLatin1String(":FILEPATH="));
break;
case CMakeConfigItem::PATH:
a.append(QLatin1String(":PATH="));
break;
case CMakeConfigItem::BOOL:
a.append(QLatin1String(":BOOL="));
break;
case CMakeConfigItem::STRING:
a.append(QLatin1String(":STRING="));
break;
case CMakeConfigItem::INTERNAL:
a.append(QLatin1String(":INTERNAL="));
break;
}
a.append(i.expandedValue(k));
return a;
});
return Utils::filtered(argList, [](const QString &s) { return !s.isEmpty(); });
}
// --------------------------------------------------------------------
// BuildDirManager:
// --------------------------------------------------------------------
BuildDirManager::BuildDirManager(const CMakeBuildConfiguration *bc) :
BuildDirManager::BuildDirManager(CMakeBuildConfiguration *bc) :
m_buildConfiguration(bc),
m_watcher(new QFileSystemWatcher(this))
{
@@ -82,7 +101,7 @@ BuildDirManager::BuildDirManager(const CMakeBuildConfiguration *bc) :
m_projectName = sourceDirectory().fileName();
m_reparseTimer.setSingleShot(true);
m_reparseTimer.setInterval(500);
m_reparseTimer.setInterval(5000);
connect(&m_reparseTimer, &QTimer::timeout, this, &BuildDirManager::parse);
connect(m_watcher, &QFileSystemWatcher::fileChanged, this, [this]() {
@@ -155,6 +174,7 @@ void BuildDirManager::resetData()
{
m_hasData = false;
m_cmakeCache.clear();
m_projectName.clear();
m_buildTargets.clear();
m_watchedFiles.clear();
@@ -177,22 +197,23 @@ bool BuildDirManager::persistCMakeState()
delete m_tempDir;
m_tempDir = nullptr;
parse();
resetData();
QTimer::singleShot(0, this, &BuildDirManager::parse); // make sure signals only happen afterwards!
return true;
}
void BuildDirManager::parse()
{
checkConfiguration();
CMakeTool *tool = CMakeKitInformation::cmakeTool(kit());
const QString generator = CMakeGeneratorKitInformation::generator(kit());
QTC_ASSERT(tool, return);
QTC_ASSERT(!generator.isEmpty(), return);
// Pop up a dialog asking the user to rerun cmake
QString cbpFile = CMakeManager::findCbpFile(QDir(workDirectory().toString()));
QFileInfo cbpFileFi(cbpFile);
const QString cbpFile = CMakeManager::findCbpFile(QDir(workDirectory().toString()));
const QFileInfo cbpFileFi = cbpFile.isEmpty() ? QFileInfo() : QFileInfo(cbpFile);
if (!cbpFileFi.exists()) {
// Initial create:
startCMake(tool, generator, intendedConfiguration());
@@ -254,21 +275,21 @@ void BuildDirManager::clearFiles()
CMakeConfig BuildDirManager::parsedConfiguration() const
{
if (!m_hasData)
return CMakeConfig();
if (m_cmakeCache.isEmpty()) {
Utils::FileName cacheFile = workDirectory();
cacheFile.appendPath(QLatin1String("CMakeCache.txt"));
if (!cacheFile.exists())
return m_cmakeCache;
QString errorMessage;
CMakeConfig result = parseConfiguration(cacheFile, &errorMessage);
m_cmakeCache = parseConfiguration(cacheFile, &errorMessage);
if (!errorMessage.isEmpty())
emit errorOccured(errorMessage);
const Utils::FileName sourceOfBuildDir
= Utils::FileName::fromUtf8(CMakeConfigItem::valueOf("CMAKE_HOME_DIRECTORY", result));
= Utils::FileName::fromUtf8(CMakeConfigItem::valueOf("CMAKE_HOME_DIRECTORY", m_cmakeCache));
if (sourceOfBuildDir != sourceDirectory()) // Use case-insensitive compare where appropriate
emit errorOccured(tr("The build directory is not for %1").arg(sourceDirectory().toUserOutput()));
return result;
}
return m_cmakeCache;
}
void BuildDirManager::stopProcess()
@@ -335,6 +356,7 @@ void BuildDirManager::extractData()
return;
m_watcher->addPath(cbpFile);
m_watcher->addPath(workDirectory().toString() + QLatin1String("/CMakeCache.txt"));
// setFolderName
CMakeCbpParser cbpparser;
@@ -497,6 +519,61 @@ void BuildDirManager::processCMakeError()
});
}
void BuildDirManager::checkConfiguration()
{
if (m_tempDir) // always throw away changes in the tmpdir!
return;
ProjectExplorer::Kit *k = m_buildConfiguration->target()->kit();
const CMakeConfig cache = parsedConfiguration();
if (cache.isEmpty())
return; // No cache file yet.
CMakeConfig newConfig;
QSet<QString> changedKeys;
QSet<QString> removedKeys;
foreach (const CMakeConfigItem &iBc, intendedConfiguration()) {
const CMakeConfigItem &iCache
= Utils::findOrDefault(cache, [&iBc](const CMakeConfigItem &i) { return i.key == iBc.key; });
if (iCache.isNull()) {
removedKeys << QString::fromUtf8(iBc.key);
} else if (QString::fromUtf8(iCache.value) != iBc.expandedValue(k)) {
changedKeys << QString::fromUtf8(iBc.key);
newConfig.append(iCache);
} else {
newConfig.append(iBc);
}
}
if (!changedKeys.isEmpty() || !removedKeys.isEmpty()) {
QSet<QString> total = removedKeys + changedKeys;
QStringList keyList = total.toList();
Utils::sort(keyList);
QString table = QLatin1String("<table>");
foreach (const QString &k, keyList) {
QString change;
if (removedKeys.contains(k))
change = tr("<removed>");
else
change = QString::fromUtf8(CMakeConfigItem::valueOf(k.toUtf8(), cache)).trimmed();
if (change.isEmpty())
change = tr("<empty>");
table += QString::fromLatin1("\n<tr><td>%1</td><td>%2</td></tr>").arg(k).arg(change.toHtmlEscaped());
}
table += QLatin1String("\n</table>");
QPointer<QMessageBox> box = new QMessageBox(Core::ICore::mainWindow());
box->setText(tr("CMake configuration has changed on disk."));
box->setInformativeText(tr("The CMakeCache.txt file has changed: %1").arg(table));
box->setStandardButtons(QMessageBox::Discard | QMessageBox::Apply);
box->setDefaultButton(QMessageBox::Discard);
int ret = box->exec();
if (ret == QMessageBox::Apply)
m_buildConfiguration->setCMakeConfiguration(newConfig);
}
}
static QByteArray trimCMakeCacheLine(const QByteArray &in) {
int start = 0;
while (start < in.count() && (in.at(start) == ' ' || in.at(start) == '\t'))
@@ -587,6 +664,8 @@ CMakeConfig BuildDirManager::parseConfiguration(const Utils::FileName &cacheFile
void BuildDirManager::maybeForceReparse()
{
checkConfiguration();
const QByteArray GENERATOR_KEY = "CMAKE_GENERATOR";
const QByteArray EXTRA_GENERATOR_KEY = "CMAKE_EXTRA_GENERATOR";
const QByteArray CMAKE_COMMAND_KEY = "CMAKE_COMMAND";

View File

@@ -63,7 +63,7 @@ class BuildDirManager : public QObject
Q_OBJECT
public:
BuildDirManager(const CMakeBuildConfiguration *bc);
BuildDirManager(CMakeBuildConfiguration *bc);
~BuildDirManager() override;
const ProjectExplorer::Kit *kit() const;
@@ -87,6 +87,8 @@ public:
void clearFiles();
CMakeConfig parsedConfiguration() const;
void checkConfiguration();
static CMakeConfig parseConfiguration(const Utils::FileName &cacheFile,
QString *errorMessage);
@@ -106,12 +108,12 @@ private:
void processCMakeOutput();
void processCMakeError();
bool m_hasData = false;
const CMakeBuildConfiguration *m_buildConfiguration = nullptr;
CMakeBuildConfiguration *m_buildConfiguration = nullptr;
Utils::QtcProcess *m_cmakeProcess = nullptr;
QTemporaryDir *m_tempDir = nullptr;
mutable CMakeConfig m_cmakeCache;
QSet<Utils::FileName> m_watchedFiles;
QString m_projectName;

View File

@@ -156,10 +156,6 @@ void CMakeBuildConfiguration::ctor()
void CMakeBuildConfiguration::maybeForceReparse()
{
ProjectExplorer::Kit *k = target()->kit();
CMakeConfig config = cmakeConfiguration();
config.append(CMakeConfigurationKitInformation::configuration(k)); // last value wins...
setCMakeConfiguration(config);
m_buildDirManager->maybeForceReparse();
}
@@ -206,18 +202,14 @@ QList<ConfigModel::DataItem> CMakeBuildConfiguration::completeCMakeConfiguration
if (m_completeConfigurationCache.isEmpty())
m_completeConfigurationCache = m_buildDirManager->parsedConfiguration();
CMakeConfig cache = Utils::filtered(m_completeConfigurationCache,
[](const CMakeConfigItem &i) {
return i.type != CMakeConfigItem::INTERNAL
&& i.type != CMakeConfigItem::STATIC;
});
return Utils::transform(cache, [](const CMakeConfigItem &i) {
return Utils::transform(m_completeConfigurationCache,
[this](const CMakeConfigItem &i) {
ConfigModel::DataItem j;
j.key = QString::fromUtf8(i.key);
j.value = QString::fromUtf8(i.value);
j.description = QString::fromUtf8(i.documentation);
j.isAdvanced = i.isAdvanced;
j.isAdvanced = i.isAdvanced || i.type == CMakeConfigItem::INTERNAL;
switch (i.type) {
case CMakeConfigItem::FILEPATH:
j.type = ConfigModel::DataItem::FILE;
@@ -272,7 +264,6 @@ void CMakeBuildConfiguration::setCurrentCMakeConfiguration(const QList<ConfigMod
return ni;
});
// There is a buildDirManager, so there must also be an active BC:
const CMakeConfig config = cmakeConfiguration() + newConfig;
setCMakeConfiguration(config);
@@ -303,11 +294,28 @@ static CMakeConfig removeDuplicates(const CMakeConfig &config)
void CMakeBuildConfiguration::setCMakeConfiguration(const CMakeConfig &config)
{
m_configuration = removeDuplicates(config);
const Kit *k = target()->kit();
CMakeConfig kitConfig = CMakeConfigurationKitInformation::configuration(k);
bool hasKitOverride = false;
foreach (const CMakeConfigItem &i, m_configuration) {
const QString b = CMakeConfigItem::expandedValueOf(k, i.key, kitConfig);
if (!b.isNull() && i.expandedValue(k) != b) {
hasKitOverride = true;
break;
}
}
if (hasKitOverride)
setWarning(tr("CMake Configuration set by the Kit was overridden in the project."));
else
setWarning(QString());
}
CMakeConfig CMakeBuildConfiguration::cmakeConfiguration() const
{
return m_configuration;
return removeDuplicates(CMakeConfigurationKitInformation::configuration(target()->kit())
+ m_configuration);
}
void CMakeBuildConfiguration::setError(const QString &message)
@@ -319,11 +327,24 @@ void CMakeBuildConfiguration::setError(const QString &message)
emit errorOccured(m_error);
}
void CMakeBuildConfiguration::setWarning(const QString &message)
{
if (m_warning == message)
return;
m_warning = message;
emit warningOccured(m_warning);
}
QString CMakeBuildConfiguration::error() const
{
return m_error;
}
QString CMakeBuildConfiguration::warning() const
{
return m_warning;
}
ProjectExplorer::NamedWidget *CMakeBuildConfiguration::createConfigWidget()
{
return new CMakeBuildSettingsWidget(this);
@@ -475,7 +496,6 @@ CMakeBuildInfo *CMakeBuildConfigurationFactory::createBuildInfo(const ProjectExp
auto info = new CMakeBuildInfo(this);
info->kitId = k->id();
info->sourceDirectory = sourceDir;
info->configuration = CMakeConfigurationKitInformation::configuration(k);
CMakeConfigItem buildTypeItem;
switch (buildType) {

View File

@@ -68,6 +68,7 @@ public:
CMakeConfig cmakeConfiguration() const;
QString error() const;
QString warning() const;
BuildDirManager *buildDirManager() const;
@@ -83,6 +84,7 @@ public:
signals:
void errorOccured(const QString &message);
void warningOccured(const QString &message);
void parsingStarted();
void dataAvailable();
@@ -97,9 +99,11 @@ private:
void setCurrentCMakeConfiguration(const QList<ConfigModel::DataItem> &items);
void setError(const QString &message);
void setWarning(const QString &message);
CMakeConfig m_configuration;
QString m_error;
QString m_warning;
mutable QList<CMakeConfigItem> m_completeConfigurationCache;

View File

@@ -48,6 +48,7 @@
#include <QPushButton>
#include <QSortFilterProxyModel>
#include <QSpacerItem>
#include <QMenu>
namespace CMakeProjectManager {
namespace Internal {
@@ -108,6 +109,17 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc)
boxLayout->addWidget(m_errorMessageLabel);
mainLayout->addLayout(boxLayout, row, 0, 1, 3, Qt::AlignHCenter);
++row;
m_warningLabel = new QLabel;
m_warningLabel->setPixmap(Core::Icons::WARNING.pixmap());
m_warningLabel->setVisible(false);
m_warningMessageLabel = new QLabel;
m_warningMessageLabel->setVisible(false);
auto boxLayout2 = new QHBoxLayout;
boxLayout2->addWidget(m_warningLabel);
boxLayout2->addWidget(m_warningMessageLabel);
mainLayout->addLayout(boxLayout2, row, 0, 1, 3, Qt::AlignHCenter);
++row;
mainLayout->addItem(new QSpacerItem(20, 10), row, 0);
@@ -142,6 +154,20 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc)
mainLayout->addWidget(findWrapper, row, 0, 1, 2);
auto buttonLayout = new QVBoxLayout;
m_addButton = new QPushButton(tr("&Add"));
buttonLayout->addWidget(m_addButton);
{
m_addButtonMenu = new QMenu;
m_addButtonMenu->addAction(tr("&Boolean"))->setData(
QVariant::fromValue(static_cast<int>(ConfigModel::DataItem::BOOLEAN)));
m_addButtonMenu->addAction(tr("&String"))->setData(
QVariant::fromValue(static_cast<int>(ConfigModel::DataItem::STRING)));
m_addButtonMenu->addAction(tr("&Directory"))->setData(
QVariant::fromValue(static_cast<int>(ConfigModel::DataItem::DIRECTORY)));
m_addButtonMenu->addAction(tr("&File"))->setData(
QVariant::fromValue(static_cast<int>(ConfigModel::DataItem::FILE)));
m_addButton->setMenu(m_addButtonMenu);
}
m_editButton = new QPushButton(tr("&Edit"));
buttonLayout->addWidget(m_editButton);
m_resetButton = new QPushButton(tr("&Reset"));
@@ -161,6 +187,7 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc)
updateAdvancedCheckBox();
setError(bc->error());
setWarning(bc->warning());
connect(project, &CMakeProject::parsingStarted, this, [this]() {
updateButtonState();
@@ -201,24 +228,49 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc)
m_configView->setCurrentIndex(idx);
m_configView->edit(idx);
});
connect(m_addButtonMenu, &QMenu::triggered, this, [this](QAction *action) {
ConfigModel::DataItem::Type type =
static_cast<ConfigModel::DataItem::Type>(action->data().value<int>());
QString value = tr("<UNSET>");
if (type == ConfigModel::DataItem::BOOLEAN)
value = QString::fromLatin1("OFF");
m_configModel->appendConfiguration(tr("<UNSET>"), value, type);
QModelIndex idx;
idx = m_configView->model()->index(
m_configView->model()->rowCount(idx) - 1, 0);
m_configView->setCurrentIndex(idx);
m_configView->edit(idx);
});
connect(bc, &CMakeBuildConfiguration::errorOccured, this, &CMakeBuildSettingsWidget::setError);
connect(bc, &CMakeBuildConfiguration::warningOccured, this, &CMakeBuildSettingsWidget::setWarning);
}
void CMakeBuildSettingsWidget::setError(const QString &message)
{
bool showWarning = !message.isEmpty();
m_errorLabel->setVisible(showWarning);
bool showError = !message.isEmpty();
m_errorLabel->setVisible(showError);
m_errorLabel->setToolTip(message);
m_errorMessageLabel->setVisible(showWarning);
m_errorMessageLabel->setVisible(showError);
m_errorMessageLabel->setText(message);
m_errorMessageLabel->setToolTip(message);
m_configView->setVisible(!showWarning);
m_editButton->setVisible(!showWarning);
m_resetButton->setVisible(!showWarning);
m_showAdvancedCheckBox->setVisible(!showWarning);
m_reconfigureButton->setVisible(!showWarning);
m_configView->setVisible(!showError);
m_editButton->setVisible(!showError);
m_resetButton->setVisible(!showError);
m_showAdvancedCheckBox->setVisible(!showError);
m_reconfigureButton->setVisible(!showError);
}
void CMakeBuildSettingsWidget::setWarning(const QString &message)
{
bool showWarning = !message.isEmpty();
m_warningLabel->setVisible(showWarning);
m_warningLabel->setToolTip(message);
m_warningMessageLabel->setVisible(showWarning);
m_warningMessageLabel->setText(message);
m_warningMessageLabel->setToolTip(message);
}
void CMakeBuildSettingsWidget::updateButtonState()

View File

@@ -37,6 +37,7 @@ class QLabel;
class QPushButton;
class QTreeView;
class QSortFilterProxyModel;
class QMenu;
QT_END_NAMESPACE
namespace CMakeProjectManager {
@@ -54,6 +55,7 @@ public:
CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc);
void setError(const QString &message);
void setWarning(const QString &message);
private:
void updateButtonState();
@@ -64,13 +66,17 @@ private:
ConfigModel *m_configModel;
QSortFilterProxyModel *m_configFilterModel;
Utils::ProgressIndicator *m_progressIndicator;
QPushButton *m_addButton;
QMenu *m_addButtonMenu;
QPushButton *m_editButton;
QPushButton *m_resetButton;
QCheckBox *m_showAdvancedCheckBox;
QPushButton *m_reconfigureButton;
QTimer m_showProgressTimer;
QLabel *m_errorLabel;
QLabel *m_warningLabel;
QLabel *m_errorMessageLabel;
QLabel *m_warningMessageLabel;
};
} // namespace Internal

View File

@@ -25,6 +25,9 @@
#include "cmakeconfigitem.h"
#include <projectexplorer/kit.h>
#include <utils/macroexpander.h>
#include <utils/qtcassert.h>
#include <QString>
@@ -60,6 +63,21 @@ QByteArray CMakeConfigItem::valueOf(const QByteArray &key, const QList<CMakeConf
return QByteArray();
}
QString CMakeConfigItem::expandedValueOf(const ProjectExplorer::Kit *k, const QByteArray &key,
const QList<CMakeConfigItem> &input)
{
for (auto it = input.constBegin(); it != input.constEnd(); ++it) {
if (it->key == key)
return it->expandedValue(k);
}
return QString();
}
QString CMakeConfigItem::expandedValue(const ProjectExplorer::Kit *k) const
{
return k->macroExpander()->expand(QString::fromUtf8(value));
}
std::function<bool (const CMakeConfigItem &a, const CMakeConfigItem &b)> CMakeConfigItem::sortOperator()
{
return [](const CMakeConfigItem &a, const CMakeConfigItem &b) { return a.key < b.key; };

View File

@@ -30,6 +30,8 @@
#include <functional>
namespace ProjectExplorer { class Kit; }
namespace CMakeProjectManager {
class CMakeConfigItem {
@@ -41,9 +43,12 @@ public:
CMakeConfigItem(const QByteArray &k, const QByteArray &v);
static QByteArray valueOf(const QByteArray &key, const QList<CMakeConfigItem> &input);
static QString expandedValueOf(const ProjectExplorer::Kit *k, const QByteArray &key,
const QList<CMakeConfigItem> &input);
bool isNull() const { return key.isEmpty(); }
QString expandedValue(const ProjectExplorer::Kit *k) const;
static std::function<bool(const CMakeConfigItem &a, const CMakeConfigItem &b)> sortOperator();
static CMakeConfigItem fromString(const QString &s);
QString toString() const;

View File

@@ -341,9 +341,11 @@ void CMakeProject::runCMake()
return;
BuildDirManager *bdm = bc->buildDirManager();
if (bdm && !bdm->isParsing())
if (bdm && !bdm->isParsing()) {
bdm->checkConfiguration();
bdm->forceReparse();
}
}
QList<CMakeBuildTarget> CMakeProject::buildTargets() const
{

View File

@@ -35,7 +35,8 @@ namespace CMakeProjectManager {
static bool isTrue(const QString &value)
{
const QString lower = value.toLower();
return lower == QStringLiteral("true") || lower == QStringLiteral("on") || lower == QStringLiteral("1");
return lower == QStringLiteral("true") || lower == QStringLiteral("on")
|| lower == QStringLiteral("1") || lower == QStringLiteral("yes");
}
ConfigModel::ConfigModel(QObject *parent) : QAbstractTableModel(parent)
@@ -71,7 +72,10 @@ Qt::ItemFlags ConfigModel::flags(const QModelIndex &index) const
else
return Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable;
} else {
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
if (item.isUserNew)
return flags |= Qt::ItemIsEditable;
return flags;
}
}
@@ -163,7 +167,7 @@ bool ConfigModel::setData(const QModelIndex &index, const QVariant &value, int r
InternalDataItem &item = itemAtRow(index.row());
switch (index.column()) {
case 0:
if (!item.key.isEmpty())
if (!item.key.isEmpty() && !item.isUserNew)
return false;
item.key = newValue;
item.isUserNew = true;
@@ -202,6 +206,25 @@ QVariant ConfigModel::headerData(int section, Qt::Orientation orientation, int r
}
}
void ConfigModel::appendConfiguration(const QString &key,
const QString &value,
const ConfigModel::DataItem::Type type,
const QString &description)
{
DataItem item;
item.key = key;
item.type = type;
item.value = value;
item.description = description;
InternalDataItem internalItem(item);
internalItem.isUserNew = true;
beginResetModel();
m_configuration.append(internalItem);
endResetModel();
}
void ConfigModel::setConfiguration(const QList<ConfigModel::DataItem> &config)
{
QList<DataItem> tmp = config;

View File

@@ -55,6 +55,10 @@ public:
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
void appendConfiguration(const QString &key,
const QString &value = QString(),
const DataItem::Type type = DataItem::UNKNOWN,
const QString &description = QString());
void setConfiguration(const QList<DataItem> &config);
void flush();
void resetAllChanges();

View File

@@ -1221,7 +1221,11 @@ void DebuggerEngine::notifyDebuggerProcessFinished(int exitCode,
notifyEngineSpontaneousShutdown();
break;
default: {
notifyEngineIll(); // Initiate shutdown sequence
// Initiate shutdown sequence
if (isMasterEngine())
notifyEngineIll();
else
masterEngine()->notifyInferiorIll();
const QString msg = exitStatus == QProcess::CrashExit ?
tr("The %1 process terminated.") :
tr("The %2 process terminated unexpectedly (exit code %1).").arg(exitCode);

View File

@@ -1129,9 +1129,16 @@ void QmlEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages
}
} else if (d->unpausedEvaluate) {
d->evaluate(command, CB(d->handleExecuteDebuggerCommand));
} else {
QModelIndex currentIndex = inspectorView()->currentIndex();
quint32 queryId = d->inspectorAgent.queryExpressionResult(
watchHandler()->watchItem(currentIndex)->id, command);
if (queryId) {
d->queryIds.append(queryId);
} else {
d->engine->showMessage(_("The application has to be stopped in a breakpoint in order to"
"evaluate expressions").arg(command), ConsoleOutput);
" evaluate expressions"), ConsoleOutput);
}
}
}

View File

@@ -261,13 +261,6 @@ QVariantMap DefaultPropertyProvider::autoGeneratedProperties(const ProjectExplor
data.insert(QLatin1String(CPP_PLATFORMLINKERFLAGS), gcc->platformLinkerFlags());
}
// TODO: Remove this once compiler version properties are set for MSVC
if (targetAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2013Flavor
|| targetAbi.osFlavor() == ProjectExplorer::Abi::WindowsMsvc2015Flavor) {
const QLatin1String flags("/FS");
data.insert(QLatin1String(CPP_PLATFORMCFLAGS), flags);
data.insert(QLatin1String(CPP_PLATFORMCXXFLAGS), flags);
}
return data;
}

View File

@@ -114,8 +114,7 @@ bool QbsBuildStep::init(QList<const BuildStep *> &earlierSteps)
connect(m_parser, SIGNAL(addOutput(QString,ProjectExplorer::BuildStep::OutputFormat)),
this, SIGNAL(addOutput(QString,ProjectExplorer::BuildStep::OutputFormat)));
connect(m_parser, SIGNAL(addTask(ProjectExplorer::Task)),
this, SIGNAL(addTask(ProjectExplorer::Task)));
connect(m_parser, &ProjectExplorer::IOutputParser::addTask, this, &QbsBuildStep::addTask);
return true;
}

View File

@@ -41,8 +41,6 @@ const char CPP_COMPILERVERSIONMAJOR[] = "cpp.compilerVersionMajor";
const char CPP_COMPILERVERSIONMINOR[] = "cpp.compilerVersionMinor";
const char CPP_COMPILERVERSIONPATCH[] = "cpp.compilerVersionPatch";
const char CPP_LINKERNAME[] = "cpp.linkerName";
const char CPP_PLATFORMCFLAGS[] = "cpp.platformCFlags";
const char CPP_PLATFORMCXXFLAGS[] = "cpp.platformCxxFlags";
const char CPP_PLATFORMCOMMONCOMPILERFLAGS[] = "cpp.platformCommonCompilerFlags";
const char CPP_PLATFORMLINKERFLAGS[] = "cpp.platformLinkerFlags";
const char CPP_PLATFORMPATH[] = "cpp.platformPath";

View File

@@ -149,197 +149,273 @@ typedef int (MDEV_API *AMDeviceSecureInstallApplicationPtr)(int, AMDeviceRef, CF
static QString mobileDeviceErrorString(am_res_t code)
{
static const char *errorStrings[] = {
"kAMDSuccess", // 0x0
"kAMDSuccess", // 0x00000000
"kAMDUndefinedError", // 0xe8000001
"kAMDBadHeaderError",
"kAMDNoResourcesError",
"kAMDReadError",
"kAMDWriteError",
"kAMDUnknownPacketError",
"kAMDInvalidArgumentError",
"kAMDNotFoundError",
"kAMDIsDirectoryError",
"kAMDPermissionError",
"kAMDNotConnectedError",
"kAMDTimeOutError",
"kAMDOverrunError",
"kAMDEOFError",
"kAMDUnsupportedError",
"kAMDFileExistsError",
"kAMDBusyError",
"kAMDCryptoError",
"kAMDInvalidResponseError",
"kAMDMissingKeyError",
"kAMDMissingValueError",
"kAMDGetProhibitedError",
"kAMDSetProhibitedError",
"kAMDRemoveProhibitedError",
"kAMDImmutableValueError",
"kAMDPasswordProtectedError",
"kAMDMissingHostIDError",
"kAMDInvalidHostIDError",
"kAMDSessionActiveError",
"kAMDSessionInactiveError",
"kAMDMissingSessionIDError",
"kAMDInvalidSessionIDError",
"kAMDMissingServiceError",
"kAMDInvalidServiceError",
"kAMDInvalidCheckinError",
"kAMDCheckinTimeoutError",
"kAMDMissingPairRecordError",
"kAMDInvalidActivationRecordError",
"kAMDMissingActivationRecordError",
"kAMDWrongDroidError",
"kAMDSUVerificationError",
"kAMDSUPatchError",
"kAMDSUFirmwareError",
"kAMDProvisioningProfileNotValid",
"kAMDSendMessageError",
"kAMDReceiveMessageError",
"kAMDMissingOptionsError",
"kAMDMissingImageTypeError",
"kAMDDigestFailedError",
"kAMDStartServiceError",
"kAMDInvalidDiskImageError",
"kAMDMissingDigestError",
"kAMDMuxError",
"kAMDApplicationAlreadyInstalledError",
"kAMDApplicationMoveFailedError",
"kAMDApplicationSINFCaptureFailedError",
"kAMDApplicationSandboxFailedError",
"kAMDApplicationVerificationFailedError",
"kAMDArchiveDestructionFailedError",
"kAMDBundleVerificationFailedError",
"kAMDCarrierBundleCopyFailedError",
"kAMDCarrierBundleDirectoryCreationFailedError",
"kAMDCarrierBundleMissingSupportedSIMsError",
"kAMDCommCenterNotificationFailedError",
"kAMDContainerCreationFailedError",
"kAMDContainerP0wnFailedError",
"kAMDContainerRemovalFailedError",
"kAMDEmbeddedProfileInstallFailedError",
"kAMDErrorError",
"kAMDExecutableTwiddleFailedError",
"kAMDExistenceCheckFailedError",
"kAMDInstallMapUpdateFailedError",
"kAMDManifestCaptureFailedError",
"kAMDMapGenerationFailedError",
"kAMDMissingBundleExecutableError",
"kAMDMissingBundleIdentifierError",
"kAMDMissingBundlePathError",
"kAMDMissingContainerError",
"kAMDNotificationFailedError",
"kAMDPackageExtractionFailedError",
"kAMDPackageInspectionFailedError",
"kAMDPackageMoveFailedError",
"kAMDPathConversionFailedError",
"kAMDRestoreContainerFailedError",
"kAMDSeatbeltProfileRemovalFailedError",
"kAMDStageCreationFailedError",
"kAMDSymlinkFailedError",
"kAMDiTunesArtworkCaptureFailedError",
"kAMDiTunesMetadataCaptureFailedError",
"kAMDAlreadyArchivedError",
"kAMDServiceLimitError",
"kAMDInvalidPairRecordError",
"kAMDServiceProhibitedError",
"kAMDCheckinSetupFailedError",
"kAMDCheckinConnectionFailedError",
"kAMDCheckinReceiveFailedError",
"kAMDCheckinResponseFailedError",
"kAMDCheckinSendFailedError",
"kAMDMuxCreateListenerError",
"kAMDMuxGetListenerError",
"kAMDMuxConnectError",
"kAMDUnknownCommandError",
"kAMDAPIInternalError",
"kAMDSavePairRecordFailedError",
"kAMDCheckinOutOfMemoryError",
"kAMDDeviceTooNewError",
"kAMDDeviceRefNoGood",
"kAMDCannotTranslateError",
"kAMDMobileImageMounterMissingImageSignature",
"kAMDMobileImageMounterResponseCreationFailed",
"kAMDMobileImageMounterMissingImageType",
"kAMDMobileImageMounterMissingImagePath",
"kAMDMobileImageMounterImageMapLoadFailed",
"kAMDMobileImageMounterAlreadyMounted",
"kAMDMobileImageMounterImageMoveFailed",
"kAMDMobileImageMounterMountPathMissing",
"kAMDMobileImageMounterMountPathNotEmpty",
"kAMDMobileImageMounterImageMountFailed",
"kAMDMobileImageMounterTrustCacheLoadFailed",
"kAMDMobileImageMounterDigestFailed",
"kAMDMobileImageMounterDigestCreationFailed",
"kAMDMobileImageMounterImageVerificationFailed",
"kAMDMobileImageMounterImageInfoCreationFailed",
"kAMDMobileImageMounterImageMapStoreFailed",
"kAMDBonjourSetupError",
"kAMDDeviceOSVersionTooLow",
"kAMDNoWifiSyncSupportError",
"kAMDDeviceFamilyNotSupported",
"kAMDEscrowLockedError",
"kAMDPairingProhibitedError",
"kAMDProhibitedBySupervision",
"kAMDDeviceDisconnectedError",
"kAMDTooBigError",
"kAMDPackagePatchFailedError",
"kAMDIncorrectArchitectureError",
"kAMDPluginCopyFailedError",
"kAMDBreadcrumbFailedError",
"kAMDBreadcrumbUnlockError",
"kAMDGeoJSONCaptureFailedError",
"kAMDNewsstandArtworkCaptureFailedError",
"kAMDMissingCommandError",
"kAMDNotEntitledError",
"kAMDMissingPackagePathError",
"kAMDMissingContainerPathError",
"kAMDMissingApplicationIdentifierError",
"kAMDMissingAttributeValueError",
"kAMDLookupFailedError",
"kAMDDictCreationFailedError",
"kAMDUserDeniedPairingError",
"kAMDPairingDialogResponsePendingError",
"kAMDInstallProhibitedError",
"kAMDUninstallProhibitedError",
"kAMDFMiPProtectedError",
"kAMDMCProtected",
"kAMDMCChallengeRequired",
"kAMDMissingBundleVersionError" // 0xe800009c
"kAMDBadHeaderError", // 0xe8000002
"kAMDNoResourcesError", // 0xe8000003
"kAMDReadError", // 0xe8000004
"kAMDWriteError", // 0xe8000005
"kAMDUnknownPacketError", // 0xe8000006
"kAMDInvalidArgumentError", // 0xe8000007
"kAMDNotFoundError", // 0xe8000008
"kAMDIsDirectoryError", // 0xe8000009
"kAMDPermissionError", // 0xe800000a
"kAMDNotConnectedError", // 0xe800000b
"kAMDTimeOutError", // 0xe800000c
"kAMDOverrunError", // 0xe800000d
"kAMDEOFError", // 0xe800000e
"kAMDUnsupportedError", // 0xe800000f
"kAMDFileExistsError", // 0xe8000010
"kAMDBusyError", // 0xe8000011
"kAMDCryptoError", // 0xe8000012
"kAMDInvalidResponseError", // 0xe8000013
"kAMDMissingKeyError", // 0xe8000014
"kAMDMissingValueError", // 0xe8000015
"kAMDGetProhibitedError", // 0xe8000016
"kAMDSetProhibitedError", // 0xe8000017
"kAMDRemoveProhibitedError", // 0xe8000018
"kAMDImmutableValueError", // 0xe8000019
"kAMDPasswordProtectedError", // 0xe800001a
"kAMDMissingHostIDError", // 0xe800001b
"kAMDInvalidHostIDError", // 0xe800001c
"kAMDSessionActiveError", // 0xe800001d
"kAMDSessionInactiveError", // 0xe800001e
"kAMDMissingSessionIDError", // 0xe800001f
"kAMDInvalidSessionIDError", // 0xe8000020
"kAMDMissingServiceError", // 0xe8000021
"kAMDInvalidServiceError", // 0xe8000022
"kAMDInvalidCheckinError", // 0xe8000023
"kAMDCheckinTimeoutError", // 0xe8000024
"kAMDMissingPairRecordError", // 0xe8000025
"kAMDInvalidActivationRecordError", // 0xe8000026
"kAMDMissingActivationRecordError", // 0xe8000027
"kAMDWrongDroidError", // 0xe8000028
"kAMDSUVerificationError", // 0xe8000029
"kAMDSUPatchError", // 0xe800002a
"kAMDSUFirmwareError", // 0xe800002b
"kAMDProvisioningProfileNotValid", // 0xe800002c
"kAMDSendMessageError", // 0xe800002d
"kAMDReceiveMessageError", // 0xe800002e
"kAMDMissingOptionsError", // 0xe800002f
"kAMDMissingImageTypeError", // 0xe8000030
"kAMDDigestFailedError", // 0xe8000031
"kAMDStartServiceError", // 0xe8000032
"kAMDInvalidDiskImageError", // 0xe8000033
"kAMDMissingDigestError", // 0xe8000034
"kAMDMuxError", // 0xe8000035
"kAMDApplicationAlreadyInstalledError", // 0xe8000036
"kAMDApplicationMoveFailedError", // 0xe8000037
"kAMDApplicationSINFCaptureFailedError", // 0xe8000038
"kAMDApplicationSandboxFailedError", // 0xe8000039
"kAMDApplicationVerificationFailedError", // 0xe800003a
"kAMDArchiveDestructionFailedError", // 0xe800003b
"kAMDBundleVerificationFailedError", // 0xe800003c
"kAMDCarrierBundleCopyFailedError", // 0xe800003d
"kAMDCarrierBundleDirectoryCreationFailedError", // 0xe800003e
"kAMDCarrierBundleMissingSupportedSIMsError", // 0xe800003f
"kAMDCommCenterNotificationFailedError", // 0xe8000040
"kAMDContainerCreationFailedError", // 0xe8000041
"kAMDContainerP0wnFailedError", // 0xe8000042
"kAMDContainerRemovalFailedError", // 0xe8000043
"kAMDEmbeddedProfileInstallFailedError", // 0xe8000044
"kAMDErrorError", // 0xe8000045
"kAMDExecutableTwiddleFailedError", // 0xe8000046
"kAMDExistenceCheckFailedError", // 0xe8000047
"kAMDInstallMapUpdateFailedError", // 0xe8000048
"kAMDManifestCaptureFailedError", // 0xe8000049
"kAMDMapGenerationFailedError", // 0xe800004a
"kAMDMissingBundleExecutableError", // 0xe800004b
"kAMDMissingBundleIdentifierError", // 0xe800004c
"kAMDMissingBundlePathError", // 0xe800004d
"kAMDMissingContainerError", // 0xe800004e
"kAMDNotificationFailedError", // 0xe800004f
"kAMDPackageExtractionFailedError", // 0xe8000050
"kAMDPackageInspectionFailedError", // 0xe8000051
"kAMDPackageMoveFailedError", // 0xe8000052
"kAMDPathConversionFailedError", // 0xe8000053
"kAMDRestoreContainerFailedError", // 0xe8000054
"kAMDSeatbeltProfileRemovalFailedError", // 0xe8000055
"kAMDStageCreationFailedError", // 0xe8000056
"kAMDSymlinkFailedError", // 0xe8000057
"kAMDiTunesArtworkCaptureFailedError", // 0xe8000058
"kAMDiTunesMetadataCaptureFailedError", // 0xe8000059
"kAMDAlreadyArchivedError", // 0xe800005a
"kAMDServiceLimitError", // 0xe800005b
"kAMDInvalidPairRecordError", // 0xe800005c
"kAMDServiceProhibitedError", // 0xe800005d
"kAMDCheckinSetupFailedError", // 0xe800005e
"kAMDCheckinConnectionFailedError", // 0xe800005f
"kAMDCheckinReceiveFailedError", // 0xe8000060
"kAMDCheckinResponseFailedError", // 0xe8000061
"kAMDCheckinSendFailedError", // 0xe8000062
"kAMDMuxCreateListenerError", // 0xe8000063
"kAMDMuxGetListenerError", // 0xe8000064
"kAMDMuxConnectError", // 0xe8000065
"kAMDUnknownCommandError", // 0xe8000066
"kAMDAPIInternalError", // 0xe8000067
"kAMDSavePairRecordFailedError", // 0xe8000068
"kAMDCheckinOutOfMemoryError", // 0xe8000069
"kAMDDeviceTooNewError", // 0xe800006a
"kAMDDeviceRefNoGood", // 0xe800006b
"kAMDCannotTranslateError", // 0xe800006c
"kAMDMobileImageMounterMissingImageSignature", // 0xe800006d
"kAMDMobileImageMounterResponseCreationFailed", // 0xe800006e
"kAMDMobileImageMounterMissingImageType", // 0xe800006f
"kAMDMobileImageMounterMissingImagePath", // 0xe8000070
"kAMDMobileImageMounterImageMapLoadFailed", // 0xe8000071
"kAMDMobileImageMounterAlreadyMounted", // 0xe8000072
"kAMDMobileImageMounterImageMoveFailed", // 0xe8000073
"kAMDMobileImageMounterMountPathMissing", // 0xe8000074
"kAMDMobileImageMounterMountPathNotEmpty", // 0xe8000075
"kAMDMobileImageMounterImageMountFailed", // 0xe8000076
"kAMDMobileImageMounterTrustCacheLoadFailed", // 0xe8000077
"kAMDMobileImageMounterDigestFailed", // 0xe8000078
"kAMDMobileImageMounterDigestCreationFailed", // 0xe8000079
"kAMDMobileImageMounterImageVerificationFailed", // 0xe800007a
"kAMDMobileImageMounterImageInfoCreationFailed", // 0xe800007b
"kAMDMobileImageMounterImageMapStoreFailed", // 0xe800007c
"kAMDBonjourSetupError", // 0xe800007d
"kAMDDeviceOSVersionTooLow", // 0xe800007e
"kAMDNoWifiSyncSupportError", // 0xe800007f
"kAMDDeviceFamilyNotSupported", // 0xe8000080
"kAMDEscrowLockedError", // 0xe8000081
"kAMDPairingProhibitedError", // 0xe8000082
"kAMDProhibitedBySupervision", // 0xe8000083
"kAMDDeviceDisconnectedError", // 0xe8000084
"kAMDTooBigError", // 0xe8000085
"kAMDPackagePatchFailedError", // 0xe8000086
"kAMDIncorrectArchitectureError", // 0xe8000087
"kAMDPluginCopyFailedError", // 0xe8000088
"kAMDBreadcrumbFailedError", // 0xe8000089
"kAMDBreadcrumbUnlockError", // 0xe800008a
"kAMDGeoJSONCaptureFailedError", // 0xe800008b
"kAMDNewsstandArtworkCaptureFailedError", // 0xe800008c
"kAMDMissingCommandError", // 0xe800008d
"kAMDNotEntitledError", // 0xe800008e
"kAMDMissingPackagePathError", // 0xe800008f
"kAMDMissingContainerPathError", // 0xe8000090
"kAMDMissingApplicationIdentifierError", // 0xe8000091
"kAMDMissingAttributeValueError", // 0xe8000092
"kAMDLookupFailedError", // 0xe8000093
"kAMDDictCreationFailedError", // 0xe8000094
"kAMDUserDeniedPairingError", // 0xe8000095
"kAMDPairingDialogResponsePendingError", // 0xe8000096
"kAMDInstallProhibitedError", // 0xe8000097
"kAMDUninstallProhibitedError", // 0xe8000098
"kAMDFMiPProtectedError", // 0xe8000099
"kAMDMCProtected", // 0xe800009a
"kAMDMCChallengeRequired", // 0xe800009b
"kAMDMissingBundleVersionError", // 0xe800009c
"kAMDAppBlacklistedError", // 0xe800009d
"This app contains an app extension with an illegal bundle identifier. App extension bundle identifiers must have a prefix consisting of their containing application's bundle identifier followed by a '.'.", // 0xe800009e
"If an app extension defines the XPCService key in its Info.plist, it must have a dictionary value.", // 0xe800009f
"App extensions must define the NSExtension key with a dictionary value in their Info.plist.", // 0xe80000a0
"If an app extension defines the CFBundlePackageType key in its Info.plist, it must have the value \"XPC!\".", // 0xe80000a1
"App extensions must define either NSExtensionMainStoryboard or NSExtensionPrincipalClass keys in the NSExtension dictionary in their Info.plist.", // 0xe80000a2
"If an app extension defines the NSExtensionContextClass key in the NSExtension dictionary in its Info.plist, it must have a string value containing one or more characters.", // 0xe80000a3
"If an app extension defines the NSExtensionContextHostClass key in the NSExtension dictionary in its Info.plist, it must have a string value containing one or more characters.", // 0xe80000a4
"If an app extension defines the NSExtensionViewControllerHostClass key in the NSExtension dictionary in its Info.plist, it must have a string value containing one or more characters.", // 0xe80000a5
"This app contains an app extension that does not define the NSExtensionPointIdentifier key in its Info.plist. This key must have a reverse-DNS format string value.", // 0xe80000a6
"This app contains an app extension that does not define the NSExtensionPointIdentifier key in its Info.plist with a valid reverse-DNS format string value.", // 0xe80000a7
"If an app extension defines the NSExtensionAttributes key in the NSExtension dictionary in its Info.plist, it must have a dictionary value.", // 0xe80000a8
"If an app extension defines the NSExtensionPointName key in the NSExtensionAttributes dictionary in the NSExtension dictionary in its Info.plist, it must have a string value containing one or more characters.", // 0xe80000a9
"If an app extension defines the NSExtensionPointVersion key in the NSExtensionAttributes dictionary in the NSExtension dictionary in its Info.plist, it must have a string value containing one or more characters.", // 0xe80000aa
"This app or a bundle it contains does not define the CFBundleName key in its Info.plist with a string value containing one or more characters.", // 0xe80000ab
"This app or a bundle it contains does not define the CFBundleDisplayName key in its Info.plist with a string value containing one or more characters.", // 0xe80000ac
"This app or a bundle it contains defines the CFBundleShortVersionStringKey key in its Info.plist with a non-string value or a zero-length string value.", // 0xe80000ad
"This app or a bundle it contains defines the RunLoopType key in the XPCService dictionary in its Info.plist with a non-string value or a zero-length string value.", // 0xe80000ae
"This app or a bundle it contains defines the ServiceType key in the XPCService dictionary in its Info.plist with a non-string value or a zero-length string value.", // 0xe80000af
"This application or a bundle it contains has the same bundle identifier as this application or another bundle that it contains. Bundle identifiers must be unique.", // 0xe80000b0
"This app contains an app extension that specifies an extension point identifier that is not supported on this version of iOS for the value of the NSExtensionPointIdentifier key in its Info.plist.", // 0xe80000b1
"This app contains multiple app extensions that are file providers. Apps are only allowed to contain at most a single file provider app extension.", // 0xe80000b2
"kMobileHouseArrestMissingCommand", // 0xe80000b3
"kMobileHouseArrestUnknownCommand", // 0xe80000b4
"kMobileHouseArrestMissingIdentifier", // 0xe80000b5
"kMobileHouseArrestDictionaryFailed", // 0xe80000b6
"kMobileHouseArrestInstallationLookupFailed", // 0xe80000b7
"kMobileHouseArrestApplicationLookupFailed", // 0xe80000b8
"kMobileHouseArrestMissingContainer", // 0xe80000b9
0, // 0xe80000ba
"kMobileHouseArrestPathConversionFailed", // 0xe80000bb
"kMobileHouseArrestPathMissing", // 0xe80000bc
"kMobileHouseArrestInvalidPath", // 0xe80000bd
"kAMDMismatchedApplicationIdentifierEntitlementError", // 0xe80000be
"kAMDInvalidSymlinkError", // 0xe80000bf
"kAMDNoSpaceError", // 0xe80000c0
"The WatchKit app extension must have, in its Info.plist's NSExtension dictionary's NSExtensionAttributes dictionary, the key WKAppBundleIdentifier with a value equal to the associated WatchKit app's bundle identifier.", // 0xe80000c1
"This app is not a valid AppleTV Stub App", // 0xe80000c2
"kAMDBundleiTunesMetadataVersionMismatchError", // 0xe80000c3
"kAMDInvalidiTunesMetadataPlistError", // 0xe80000c4
"kAMDMismatchedBundleIDSigningIdentifierError", // 0xe80000c5
"This app contains multiple WatchKit app extensions. Only a single WatchKit extension is allowed.", // 0xe80000c6
"A WatchKit app within this app is not a valid bundle.", // 0xe80000c7
"kAMDDeviceNotSupportedByThinningError", // 0xe80000c8
"The UISupportedDevices key in this app's Info.plist does not specify a valid set of supported devices.", // 0xe80000c9
"This app contains an app extension with an illegal bundle identifier. App extension bundle identifiers must have a prefix consisting of their containing application's bundle identifier followed by a '.', with no further '.' characters after the prefix.", // 0xe80000ca
"kAMDAppexBundleIDConflictWithOtherIdentifierError", // 0xe80000cb
"kAMDBundleIDConflictWithOtherIdentifierError", // 0xe80000cc
"This app contains multiple WatchKit 1.0 apps. Only a single WatchKit 1.0 app is allowed.", // 0xe80000cd
"This app contains multiple WatchKit 2.0 apps. Only a single WatchKit 2.0 app is allowed.", // 0xe80000ce
"The WatchKit app has an invalid stub executable.", // 0xe80000cf
"The WatchKit app has multiple app extensions. Only a single WatchKit extension is allowed in a WatchKit app, and only if this is a WatchKit 2.0 app.", // 0xe80000d0
"The WatchKit 2.0 app contains non-WatchKit app extensions. Only WatchKit app extensions are allowed in WatchKit apps.", // 0xe80000d1
"The WatchKit app has one or more embedded frameworks. Frameworks are only allowed in WatchKit app extensions in WatchKit 2.0 apps.", // 0xe80000d2
"This app contains a WatchKit 1.0 app with app extensions. This is not allowed.", // 0xe80000d3
"This app contains a WatchKit 2.0 app without an app extension. WatchKit 2.0 apps must contain a WatchKit app extension.", // 0xe80000d4
"The WatchKit app's Info.plist must have a WKCompanionAppBundleIdentifier key set to the bundle identifier of the companion app.", // 0xe80000d5
"The WatchKit app's Info.plist contains a non-string key.", // 0xe80000d6
"The WatchKit app's Info.plist contains a key that is not in the whitelist of allowed keys for a WatchKit app.", // 0xe80000d7
"The WatchKit 1.0 and a WatchKit 2.0 apps within this app must have have the same bundle identifier.", // 0xe80000d8
"This app contains a WatchKit app with an invalid bundle identifier. The bundle identifier of a WatchKit app must have a prefix consisting of the companion app's bundle identifier, followed by a '.'.", // 0xe80000d9
"This app contains a WatchKit app where the UIDeviceFamily key in its Info.plist does not specify the value 4 to indicate that it's compatible with the Apple Watch device type.", // 0xe80000da
"The device is out of storage for apps. Please remove some apps from the device and try again.", // 0xe80000db
};
static const size_t errorStringMask = 0xe8000000;
static const size_t errorStringLast = ((sizeof(errorStrings) / sizeof(char *)) - 1) | errorStringMask;
static const char *errorStrings2[] = {
0,
"An unknown error has occurred.", // 0xe8008001
"Attempted to modify an immutable provisioning profile.", // 0xe8008002
"This provisioning profile is malformed.", // 0xe8008003
"This provisioning profile does not have a valid signature (or it has a valid, but untrusted signature).", // 0xe8008004
"This provisioning profile is malformed.", // 0xe8008005
"This provisioning profile is malformed.", // 0xe8008006
"This provisioning profile is malformed.", // 0xe8008007
"This provisioning profile is malformed.", // 0xe8008008
"The signature was not valid.", // 0xe8008009
"Unable to allocate memory.", // 0xe800800a
"A file operation failed.", // 0xe800800b
"There was an error communicating with your device.", // 0xe800800c
"There was an error communicating with your device.", // 0xe800800d
"This provisioning profile does not have a valid signature (or it has a valid, but untrusted signature).", // 0xe800800e
"The application's signature is valid but it does not match the expected hash.", // 0xe800800f
"This provisioning profile is unsupported.", // 0xe8008010
"This provisioning profile has expired.", // 0xe8008011
"This provisioning profile cannot be installed on this device.", // 0xe8008012
"This provisioning profile does not have a valid signature (or it has a valid, but untrusted signature).", // 0xe8008013
"The executable contains an invalid signature.", // 0xe8008014
"A valid provisioning profile for this executable was not found.", // 0xe8008015
"The executable was signed with invalid entitlements.", // 0xe8008016
"A signed resource has been added, modified, or deleted.", // 0xe8008017
"The identity used to sign the executable is no longer valid.", // 0xe8008018
"The application does not have a valid signature.", // 0xe8008019
"This provisioning profile does not have a valid signature (or it has a valid, but untrusted signature).", // 0xe800801a
"There was an error communicating with your device.", // 0xe800801b
"No code signature found.", // 0xe800801c
"Rejected by policy.", // 0xe800801d
"The requested profile does not exist (it may have been removed).", // 0xe800801e
};
static const size_t errorString2Mask = 0xe8008000;
static const size_t errorString2Last = ((sizeof(errorStrings2) / sizeof(char *)) - 1) | errorString2Mask;
CFStringRef key = NULL;
static const size_t errorStringLast = ((sizeof(errorStrings) / sizeof(char *)) - 1) | 0xe8000000;
if (code <= errorStringLast) {
// Mask off some bits to get an index into the known error names array
key = QString::fromLatin1(errorStrings[code & ~0xe8000000]).toCFString();
} else {
// Some errors don't have constant names; check a few other known error codes
switch (code) {
case 0xe8008015:
key = CFSTR("A valid provisioning profile for this executable was not found.");
break;
case 0xe8008016:
key = CFSTR("The executable was signed with invalid entitlements.");
break;
case 0xe8008017:
key = CFSTR("A signed resource has been added, modified, or deleted.");
break;
case 0xe8008018:
key = CFSTR("The identity used to sign the executable is no longer valid.");
break;
case 0xe8008019:
key = CFSTR("The application does not have a valid signature.");
break;
case 0xe800801c:
key = CFSTR("The signature was not valid.");
break;
default:
if (code <= errorStringLast && code != 0xe80000ba)
key = QString::fromLatin1(errorStrings[code & ~errorStringMask]).toCFString();
else if (code > errorString2Mask && code <= errorString2Last)
key = QString::fromLatin1(errorStrings2[code & ~errorString2Mask]).toCFString();
else
return QString();
}
CFRetain(key);
}
CFURLRef url = QUrl::fromLocalFile(
QStringLiteral("/System/Library/PrivateFrameworks/MobileDevice.framework")).toCFURL();

View File

@@ -5989,6 +5989,18 @@ void tst_Dumpers::dumper_data()
"b.append(a);\n"
"b.append(QJsonValue(2));\n"
"\n"
"QJsonArray c;\n"
"for (unsigned int i = 0; i < 32; ++i) {\n"
" c.append(QJsonValue(qint64(1u << i) - 1));\n"
" c.append(QJsonValue(qint64(1u << i)));\n"
" c.append(QJsonValue(qint64(1u << i) + 1));\n"
"}\n"
"for (unsigned int i = 0; i < 32; ++i) {\n"
" c.append(QJsonValue(-qint64(1u << i) + 1));\n"
" c.append(QJsonValue(-qint64(1u << i)));\n"
" c.append(QJsonValue(-qint64(1u << i) - 1));\n"
"}\n"
"\n"
"unused(&ob,&b,&a);\n")
+ Cxx11Profile()
+ Check("a", "<6 items>", "@QJsonArray")
@@ -6013,6 +6025,17 @@ void tst_Dumpers::dumper_data()
+ Check("b.1.4", "[4]", "true", "QJsonValue (Bool)")
+ Check("b.1.5", "[5]", "<5 items>", "QJsonValue (Object)")
+ Check("b.2", "[2]", "2", "QJsonValue (Number)")
+ Check("c", "c", "<192 items>", "@QJsonArray")
+ Check("c.0", "[0]", "0.0", "QJsonValue (Number)")
+ Check("c.1", "[1]", "1", "QJsonValue (Number)")
+ Check("c.78", "[78]", "67108863", "QJsonValue (Number)")
+ Check("c.79", "[79]", "67108864.0", "QJsonValue (Number)")
+ Check("c.94", "[94]", "2147483648.0", "QJsonValue (Number)")
+ Check("c.95", "[95]", "2147483649.0", "QJsonValue (Number)")
+ Check("c.96", "[96]", "0.0", "QJsonValue (Number)")
+ Check("c.97", "[97]", "-1", "QJsonValue (Number)")
+ Check("c.174", "[174]", "-67108863", "QJsonValue (Number)")
+ Check("c.175", "[175]", "-67108864.0", "QJsonValue (Number)")
+ Check("ob", "ob", "<5 items>", "@QJsonObject")
+ Check("ob.0", "\"a\"", "1", "QJsonValue (Number)")
+ Check("ob.1", "\"bb\"", "2", "QJsonValue (Number)")

View File

@@ -0,0 +1,6 @@
pragma Singleton
import QtQuick 2.0
Item {
}

View File

@@ -185,6 +185,9 @@ void dummyStatement(...) {}
#include <QXmlAttributes>
#include <QHostAddress>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
#include <QNetworkRequest>
#include <array>
@@ -6014,6 +6017,41 @@ namespace qscript {
} // namespace script
namespace qjson {
void testQJson()
{
QJsonObject obj {
{"-1", -1},
{"3", 3},
{"0x3fffff (4194303)", 4194303},
{"0x400000 (4194304)", 4194304},
{"0x800000 (8388608)", 8388608},
{"0x1000000 (16777216)", 16777216},
{"-0x3fffff (-4194303)", -4194303},
{"-0x400000 (-4194304)", -4194304},
{"-0x800000 (-8388608)", -8388608}
};
QJsonArray arr;
for (unsigned int i = 0; i < 32; ++i) {
arr.append(QJsonValue(qint64(1u << i) - 1));
arr.append(QJsonValue(qint64(1u << i)));
arr.append(QJsonValue(qint64(1u << i) + 1));
}
for (unsigned int i = 0; i < 32; ++i) {
arr.append(QJsonValue(-qint64(1u << i) + 1));
arr.append(QJsonValue(-qint64(1u << i)));
arr.append(QJsonValue(-qint64(1u << i) - 1));
}
BREAK_HERE;
// Check v -1 QJsonValue.
// Check obj "foo" -1 QJsonValue.
// Continue.
}
} // namespace json
namespace webkit {
void testWTFString()
@@ -7187,6 +7225,7 @@ int main(int argc, char *argv[])
qregexp::testQRegExp();
qregion::testQRegion();
qscript::testQScript();
qjson::testQJson();
qset::testQSet();
qsharedpointer::testQSharedPointer();
qstack::testQStack();

View File

@@ -1,5 +1,5 @@
:*Qt Creator.Add Kit_QPushButton {text='Add Kit' type='QPushButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
:*Qt Creator.Build Project_Core::Internal::FancyToolButton {text='Build Project' type='Core::Internal::FancyToolButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
:*Qt Creator.Build Project_Core::Internal::FancyToolButton {text?='Build Project*' type='Core::Internal::FancyToolButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
:*Qt Creator.Cancel Build_QToolButton {text='Cancel Build' type='QToolButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
:*Qt Creator.Cancel_QPushButton {text='Cancel' type='QPushButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}
:*Qt Creator.Clear_QToolButton {text='Clear' type='QToolButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'}

View File

@@ -24,8 +24,17 @@
############################################################################
def startCreatorTryingClang():
try:
# start Qt Creator with enabled ClangCodeModel plugin (without modifying settings)
startApplication("qtcreator -load ClangCodeModel" + SettingsPath)
except RuntimeError:
t, v = sys.exc_info()[:2]
strv = str(v)
if strv == "startApplication() failed":
test.fatal("%s. Creator built without ClangCodeModel?" % strv)
else:
test.fatal("Exception caught", "%s(%s)" % (str(t), strv))
return False
errorMsg = "{type='QMessageBox' unnamed='1' visible='1' windowTitle='Qt Creator'}"
errorOK = "{text='OK' type='QPushButton' unnamed='1' visible='1' window=%s}" % errorMsg
if not waitFor("object.exists(errorOK)", 5000):

View File

@@ -345,17 +345,17 @@ def invokeContextMenuItem(editorArea, command1, command2 = None):
ctxtMenu = openContextMenuOnTextCursorPosition(editorArea)
snooze(1)
if platform.system() == 'Darwin':
activateItem(ctxtMenu, command1)
item1 = waitForObjectItem(ctxtMenu, command1)
subMenu = item1.menu()
activateItem(item1)
# subMenu could have been triggered by hovering, but closed again by clicking
if subMenu and not subMenu.visible:
activateItem(item1)
if command2:
activateItem(subMenu, command2)
else:
activateItem(waitForObjectItem(objectMap.realName(ctxtMenu), command1, 2000))
if command2:
# Hack for Squish 5.0.1 handling menus of Qt5.2 on Mac (avoids crash) - remove asap
if platform.system() == 'Darwin':
for obj in object.topLevelObjects():
if className(obj) == 'QMenu' and obj.visible and not obj == ctxtMenu:
activateItem(obj, command2)
break
else:
activateItem(waitForObjectItem("{title='%s' type='QMenu' visible='1' window=%s}"
% (command1, objectMap.realName(ctxtMenu)), command2, 2000))

View File

@@ -689,7 +689,7 @@ def copyFilesToDir(files, targetDir):
return result
def __sortFilenamesOSDependent__(filenames):
if platform.system() in ('Windows', 'Microsoft'):
if platform.system() in ('Windows', 'Microsoft', 'Darwin'):
filenames.sort(key=str.lower)
else:
filenames.sort()

View File

@@ -58,13 +58,13 @@ def ensureChecked(objectName, shouldBeChecked = True, timeout=20000):
try:
# needed for transition Qt::PartiallyChecked -> Qt::Checked -> Qt::Unchecked
clicked = 0
while not waitFor('widget.checkState() == targetState', 1000) and clicked < 2:
while not waitFor('widget.checkState() == targetState', 1500) and clicked < 2:
clickButton(widget)
clicked += 1
test.verify(waitFor("widget.checkState() == targetState", 1000))
except:
# widgets not derived from QCheckbox don't have checkState()
if not waitFor('widget.checked == shouldBeChecked', 1000):
if not waitFor('widget.checked == shouldBeChecked', 1500):
mouseClick(widget, 10, 6, 0, Qt.LeftButton)
test.verify(waitFor("widget.checked == shouldBeChecked", 1000))
test.log("New state for QCheckBox: %s" % state,
@@ -217,10 +217,11 @@ def logApplicationOutput():
# get the output from a given cmdline call
def getOutputFromCmdline(cmdline):
versCall = subprocess.Popen(cmdline, stdout=subprocess.PIPE, shell=True)
result = versCall.communicate()[0]
versCall.stdout.close()
return result
try:
return subprocess.check_output(cmdline, shell=True) # TODO: do not use shell=True
except subprocess.CalledProcessError as e:
test.warning("Command '%s' returned %d" % (e.cmd, e.returncode))
return e.output
def selectFromFileDialog(fileName, waitForFile=False):
if platform.system() == "Darwin":
@@ -228,7 +229,7 @@ def selectFromFileDialog(fileName, waitForFile=False):
nativeType("<Command+Shift+g>")
snooze(1)
nativeType(fileName)
snooze(1)
snooze(2)
nativeType("<Return>")
snooze(3)
nativeType("<Return>")
@@ -600,6 +601,7 @@ def clickOnTab(tabBarStr, tabText, timeout=5000):
if platform.system() == 'Darwin' and not tabBar.visible:
test.log("Using workaround for Mac.")
setWindowState(tabBar, WindowState.Normal)
tabBar = waitForObject(tabBarStr, 2000)
clickTab(tabBar, tabText)
waitFor("str(tabBar.tabText(tabBar.currentIndex)) == '%s'" % tabText, timeout)

View File

@@ -78,6 +78,19 @@ def main():
type(waitForObject(":popupFrame_Proposal_QListView"), "<Tab>")
test.compare(str(lineUnderCursor(editorWidget)).strip(), "void",
"Step 4: Verifying if: Word 'void' is completed because only one option is available.")
# Step 4.5: Insert text "2." to new line and verify that code completion is not triggered (QTCREATORBUG-16188)
resetLine(editorWidget)
lineWithFloat = "float fl = 2."
type(editorWidget, lineWithFloat)
try:
waitForObject(":popupFrame_Proposal_QListView", 5000)
if useClang and JIRA.isBugStillOpen(16188):
test.xfail("Typing a float value triggered code completion")
else:
test.fail("Typing a float value triggered code completion")
except:
test.compare(str(lineUnderCursor(editorWidget)), " " + lineWithFloat,
"Typing a float value does not trigger code completion")
# Step 5: From "Tools -> Options -> Text Editor -> Completion" select Activate completion Manually,
# uncheck Autocomplete common prefix and press Apply and then Ok . Return to Edit mode.
test.log("Step 5: Change Code Completion settings")

View File

@@ -31,7 +31,8 @@ def main():
# expected error texts - for different compilers
expectedErrorAlternatives = ["'SyntaxError' was not declared in this scope",
"'SyntaxError' : undeclared identifier",
"use of undeclared identifier 'SyntaxError'"]
"use of undeclared identifier 'SyntaxError'",
"unknown type name 'SyntaxError'"]
startApplication("qtcreator" + SettingsPath)
if not startedWithoutPluginError():
return

View File

@@ -49,6 +49,10 @@ def main():
waitFor("object.exists(':popupFrame_TextEditor::GenericProposalWidget')", 1500)
found = str(lineUnderCursor(cppwindow)).strip()
exp = testData.field(record, "expected")
if (useClang and exp.endswith("->") and JIRA.isBugStillOpen(16336)
and platform.system() in ('Windows', 'Microsoft')):
test.xcompare(found, exp)
else:
test.compare(found, exp)
invokeMenuItem("File", 'Revert "main.cpp" to Saved')
clickButton(waitForObject(":Revert to Saved.Proceed_QPushButton"))

View File

@@ -2,6 +2,8 @@
"" "" "syntaxError" "." "syntaxError."
"" "" "argc" "." "argc."
"" "" "argv[0]" "." "argv[0]."
"" "" "2" "." "2."
"" "" "float fl = 2" "." "float fl = 2."
"" "QCoreApplication qa;" "qa" "." "qa."
"" "QCoreApplication *p;" "p" "." "p->"
"" "QCoreApplication &ref = a;" "ref" "." "ref."
1 include declaration usage operator expected
2 syntaxError . syntaxError.
3 argc . argc.
4 argv[0] . argv[0].
5 2 . 2.
6 float fl = 2 . float fl = 2.
7 QCoreApplication qa; qa . qa.
8 QCoreApplication *p; p . p->
9 QCoreApplication &ref = a; ref . ref.

View File

@@ -117,7 +117,7 @@ def __compFunc__(it, foundComp, foundCompNames):
foundCompNames.append(it)
def __dbgFunc__(it, foundDbg):
waitFor("object.exists(':Path.Utils_BaseValidatingLineEdit')", 1000)
waitFor("object.exists(':Path.Utils_BaseValidatingLineEdit')", 2000)
pathLineEdit = findObject(":Path.Utils_BaseValidatingLineEdit")
foundDbg.append(str(pathLineEdit.text))

View File

@@ -42,6 +42,8 @@ def main():
selectFromCombo(":User Interface.languageBox_QComboBox", languageName)
clickButton(waitForObject(":Options.OK_QPushButton"))
clickButton(waitForObject(":Restart required.OK_QPushButton"))
test.verify(waitFor("not object.exists(':Options_Core::Internal::SettingsDialog')", 5000),
"Options dialog disappeared")
invokeMenuItem("File", "Exit")
waitForCleanShutdown()
snooze(4) # wait for complete unloading of Creator
@@ -49,12 +51,16 @@ def main():
startApplication("qtcreator" + SettingsPath)
try:
if platform.system() == 'Darwin':
# temporary hack for handling wrong menus when using Squish 5.0.1 with Qt5.2
try:
fileMenu = waitForObjectItem(":Qt Creator.QtCreator.MenuBar_QMenuBar",
testData.field(lang, "File"))
activateItem(fileMenu)
waitForObject("{type='QMenu' visible='1'}")
obj = waitForObject("{type='QMenu' visible='1'}")
test.compare(str(obj.objectName), 'QtCreator.Menu.File',
"Creator was running in %s translation" % languageName)
activateItem(fileMenu)
except:
test.fail("Creator seems to be missing %s translation" % languageName)
nativeType("<Command+q>")
else:
invokeMenuItem(testData.field(lang, "File"), testData.field(lang, "Exit"))

View File

@@ -52,7 +52,7 @@ def main():
type(editor, "<Return>")
invokeMenuItem("File", "Save All")
clickButton(waitForObject(":Qt Creator.CloseDoc_QToolButton"))
if test.verify(waitFor("headerFileName in str(mainWindow.windowTitle)", 1000),
if test.verify(waitFor("headerFileName in str(mainWindow.windowTitle)", 2000),
"Header file was shown after closing source?"):
editor = waitForObject(":Qt Creator_CppEditor::Internal::CPPEditorWidget")
editorText = str(editor.plainText)
@@ -80,7 +80,7 @@ def main():
addCPlusPlusFileToCurrentProject(newClassName, "C++ Class", False, newBasePath=basePath,
expectedSourceName=sourceFileName,
expectedHeaderName=headerFileName)
test.verify(not waitFor("overwritten(sourceFileName)", 500),
test.verify(not waitFor("overwritten(sourceFileName)", 2000),
"Source file should not be overwritten.")
test.verify(not waitFor("overwritten(headerFileName)", 500),
"Header file should not be overwritten.")
@@ -88,7 +88,7 @@ def main():
addCPlusPlusFileToCurrentProject(newClassName, "C++ Class", True, newBasePath=basePath,
expectedSourceName=sourceFileName,
expectedHeaderName=headerFileName)
test.verify(waitFor("overwritten(sourceFileName)", 500),
test.verify(waitFor("overwritten(sourceFileName)", 2000),
"Source file should be overwritten.")
test.verify(waitFor("overwritten(headerFileName)", 500),
"Header file should be overwritten.")

View File

@@ -33,12 +33,25 @@ def performEditMenu():
passiveLineEdit = waitForObject(":FormEditorStack.__qt__passive_editor_QLineEdit")
replaceEditorContent(passiveLineEdit, "SquishTestFile")
type(passiveLineEdit, "<Return>")
menu = waitForObject("{name='menuSquishTestFile' title='SquishTestFile' type='QDesignerMenu' "
"visible='1' window=':Qt Creator_Core::Internal::MainWindow'}")
# this "special" QDesignerMenu will be hidden and unusable on OSX
menuStr = ("{name='menuSquishTestFile' title='SquishTestFile' type='QDesignerMenu' "
"window=':Qt Creator_Core::Internal::MainWindow'}")
try:
menu = waitForObject(menuStr, 5000)
except:
if platform.system() == 'Darwin':
# we need some information of the menu, so find at least the 'hidden' one
menu = findObject(menuStr)
else:
raise
menuHeight = menu.height
itemHeight = menuHeight / 2 # actually only 'Type Here' and 'Add Separator' are shown
itemHalf = itemHeight / 2
# add Open menu item
if platform.system() == 'Darwin':
# double clicking is not possible on hidden objects
nativeType("<Return>")
else:
doubleClick(menu, 15, itemHalf, 0, Qt.LeftButton)
passiveLineEdit = waitForObject(":FormEditorStack.__qt__passive_editor_QLineEdit")
replaceEditorContent(passiveLineEdit, "Open")
@@ -46,11 +59,18 @@ def performEditMenu():
waitFor("menu.height > menuHeight", 2000)
menuHeight = menu.height
# add a separator
if platform.system() == 'Darwin':
nativeType("<Down>")
nativeType("<Return>")
else:
doubleClick(menu, 15, menu.height - itemHalf, 0, Qt.LeftButton)
waitFor("menu.height > menuHeight", 2000)
separatorHeight = menu.height - menuHeight
menuHeight = menu.height
# add Shutdown menu item (Quit/Exit do not work because Squish/Qt5 problems with menus)
if platform.system() == 'Darwin':
nativeType("<Return>")
else:
doubleClick(menu, 30, itemHeight + separatorHeight + itemHalf, 0, Qt.LeftButton)
passiveLineEdit = waitForObject(":FormEditorStack.__qt__passive_editor_QLineEdit")
replaceEditorContent(passiveLineEdit, "Shutdown")