diff options
Diffstat (limited to 'src')
213 files changed, 3795 insertions, 1750 deletions
diff --git a/src/libs/cplusplus/OverviewModel.cpp b/src/libs/cplusplus/OverviewModel.cpp index 3b42204be3..7b3649ed03 100644 --- a/src/libs/cplusplus/OverviewModel.cpp +++ b/src/libs/cplusplus/OverviewModel.cpp @@ -181,6 +181,7 @@ QVariant OverviewModel::data(const QModelIndex &index, int role) const if (Template *t = symbol->asTemplate()) if (Symbol *templateDeclaration = t->declaration()) { QStringList parameters; + parameters.reserve(t->templateParameterCount()); for (unsigned i = 0; i < t->templateParameterCount(); ++i) parameters.append(_overview.prettyName(t->templateParameterAt(i)->name())); name += QLatin1Char('<') + parameters.join(QLatin1String(", ")) + QLatin1Char('>'); diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp index 6998e7bbc4..ad1109ff0f 100644 --- a/src/libs/extensionsystem/pluginmanager.cpp +++ b/src/libs/extensionsystem/pluginmanager.cpp @@ -361,7 +361,7 @@ QReadWriteLock *PluginManager::listLock() */ void PluginManager::loadPlugins() { - return d->loadPlugins(); + d->loadPlugins(); } /*! diff --git a/src/libs/flamegraph/flamegraph.cpp b/src/libs/flamegraph/flamegraph.cpp index 79627c2dc5..eb7431d179 100644 --- a/src/libs/flamegraph/flamegraph.cpp +++ b/src/libs/flamegraph/flamegraph.cpp @@ -155,6 +155,10 @@ int FlameGraph::buildNode(const QModelIndex &parentIndex, QObject *parentObject, } } + // Root object: attribute all remaining width to "others" + if (!parentIndex.isValid()) + skipped = parentSize - position; + if (skipped > 0) { appendChild(parentObject, parentItem, context, QModelIndex(), position / parentSize, skipped / parentSize); diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp index c4c01a93fb..b5130477ad 100644 --- a/src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp +++ b/src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp @@ -147,7 +147,7 @@ void StereotypeDisplayVisitor::visitDItem(const DItem *item) m_stereotypeSmartDisplay = DObject::StereotypeIcon; visitDObject(item); if (m_stereotypeIconId.isEmpty() && !item->shape().isEmpty()) - m_shapeIconId = m_stereotypeController->findStereotypeIconId(StereotypeIcon::ElementItem, QStringList(item->shape())); + m_stereotypeIconId = m_stereotypeController->findStereotypeIconId(StereotypeIcon::ElementItem, QStringList(item->shape())); if (m_shapeIconId.isEmpty() && !item->variety().isEmpty()) m_shapeIconId = m_stereotypeController->findStereotypeIconId(StereotypeIcon::ElementItem, QStringList(item->variety())); } diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp index 01f3286ff9..62ceaad1a3 100644 --- a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp +++ b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp @@ -768,7 +768,7 @@ void PropertiesView::MView::visitMAssociation(const MAssociation *association) this, &PropertiesView::MView::onAssociationEndBKindChanged); } if (isSingleSelection) { - if ((!isValidAssociationKindIndex(m_endAKind->currentIndex()) + if ((!isValidAssociationKindIndex(m_endBKind->currentIndex()) || association->endB().kind() != translateIndexToAssociationKind(m_endBKind->currentIndex())) && !m_endBKind->hasFocus()) { m_endBKind->setCurrentIndex(translateAssociationKindToIndex(association->endB().kind())); diff --git a/src/libs/qmldebug/baseenginedebugclient.cpp b/src/libs/qmldebug/baseenginedebugclient.cpp index 7f9df78d84..f3e5271e32 100644 --- a/src/libs/qmldebug/baseenginedebugclient.cpp +++ b/src/libs/qmldebug/baseenginedebugclient.cpp @@ -193,6 +193,7 @@ void BaseEngineDebugClient::messageReceived(const QByteArray &data) int count; ds >> count; QList<EngineReference> engines; + engines.reserve(count); for (int ii = 0; ii < count; ++ii) { EngineReference eng; ds >> eng.m_name; diff --git a/src/libs/qmleditorwidgets/contextpanewidgetimage.cpp b/src/libs/qmleditorwidgets/contextpanewidgetimage.cpp index e914bdcbc9..188e19ae7d 100644 --- a/src/libs/qmleditorwidgets/contextpanewidgetimage.cpp +++ b/src/libs/qmleditorwidgets/contextpanewidgetimage.cpp @@ -482,7 +482,7 @@ void ContextPaneWidgetImage::setPixmap(const QString &fileName) if (m_borderImage) { QString localFileName(fileName); - if (QFile(fileName).exists()) { + if (QFileInfo::exists(fileName)) { if (fileName.endsWith(QLatin1String("sci"))) { QString pixmapFileName; int left = 0; @@ -527,7 +527,7 @@ void ContextPaneWidgetImage::setPixmap(const QString &fileName) } uiBorderImage->label->setPixmap(pix); } else { - if (QFile(fileName).exists()) { + if (QFileInfo::exists(fileName)) { QPixmap source(fileName); previewDialog()->setPixmap(source, 1); ui->sizeLabel->setText(QString::number(source.width()) + QLatin1Char('x') + QString::number(source.height())); @@ -703,8 +703,10 @@ static inline bool rangeCheck(int target, int pos) void PreviewLabel::mousePressEvent(QMouseEvent * event) { - if (!m_borderImage) - return QLabel::mouseMoveEvent(event); + if (!m_borderImage) { + QLabel::mouseMoveEvent(event); + return; + } bool bottom = false; @@ -790,8 +792,10 @@ static inline int limitPositive(int i) void PreviewLabel::mouseMoveEvent(QMouseEvent * event) { - if (!m_borderImage) - return QLabel::mouseMoveEvent(event); + if (!m_borderImage) { + QLabel::mouseMoveEvent(event); + return; + } QPoint p = event->pos(); bool bottom = false; diff --git a/src/libs/qmljs/parser/qmljsast_p.h b/src/libs/qmljs/parser/qmljsast_p.h index 0a06dace61..0d2b338057 100644 --- a/src/libs/qmljs/parser/qmljsast_p.h +++ b/src/libs/qmljs/parser/qmljsast_p.h @@ -223,7 +223,7 @@ public: static void accept(Node *node, Visitor *visitor); inline static void acceptChild(Node *node, Visitor *visitor) - { return accept(node, visitor); } // ### remove + { accept(node, visitor); } // ### remove virtual void accept0(Visitor *visitor) = 0; virtual SourceLocation firstSourceLocation() const = 0; diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index 66774a43f3..c88e57549e 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -124,7 +124,7 @@ public: fileName.prepend(QLatin1Char('/')); fileName.prepend(_doc->path()); } - if (!QFileInfo(fileName).exists()) + if (!QFileInfo::exists(fileName)) setMessage(WarnFileOrDirectoryDoesNotExist); } } @@ -556,8 +556,7 @@ public: "Scale", "Translate", "Package", - "Particles", - "Dialog"}) + "Particles"}) { } diff --git a/src/libs/qmljs/qmljsconstants.h b/src/libs/qmljs/qmljsconstants.h index 532b74697d..9cbf9fb19b 100644 --- a/src/libs/qmljs/qmljsconstants.h +++ b/src/libs/qmljs/qmljsconstants.h @@ -56,11 +56,12 @@ enum Enum { namespace Severity { enum Enum { - Hint, // cosmetic or convention - MaybeWarning, // possibly a warning, insufficient information - Warning, // could cause unintended behavior - MaybeError, // possibly an error, insufficient information - Error // definitely an error + Hint, // cosmetic or convention + MaybeWarning, // possibly a warning, insufficient information + Warning, // could cause unintended behavior + ReadingTypeInfoWarning, // currently dumping type information + MaybeError, // possibly an error, insufficient information + Error // definitely an error }; } diff --git a/src/libs/qmljs/qmljsdescribevalue.cpp b/src/libs/qmljs/qmljsdescribevalue.cpp index ad9b2c1337..268266ffcc 100644 --- a/src/libs/qmljs/qmljsdescribevalue.cpp +++ b/src/libs/qmljs/qmljsdescribevalue.cpp @@ -325,20 +325,18 @@ void DescribeValueVisitor::visit(const ObjectValue *value) basicDump("ObjectValue", value, printDetail); } if (printDetail) { - if (value) { - dumpNewline(); - dump("className:"); - dump(value->className()); - dumpNewline(); - dump("members:"); - openContext("["); - PrintMembers printMembers(*this); - value->processMembers(&printMembers); - closeContext("]"); - dumpNewline(); - dump("prototype:"); - (*this)(value->prototype()); - } + dumpNewline(); + dump("className:"); + dump(value->className()); + dumpNewline(); + dump("members:"); + openContext("["); + PrintMembers printMembers(*this); + value->processMembers(&printMembers); + closeContext("]"); + dumpNewline(); + dump("prototype:"); + (*this)(value->prototype()); closeContext(); } --m_depth; diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index 1f436160c0..eeceb6a786 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -453,8 +453,9 @@ bool LinkPrivate::importLibrary(Document::Ptr doc, } } if (errorLoc.isValid()) { - warning(doc, errorLoc, - Link::tr("QML module contains C++ plugins, currently reading type information...")); + appendDiagnostic(doc, DiagnosticMessage(Severity::ReadingTypeInfoWarning, + errorLoc, + Link::tr("QML module contains C++ plugins, currently reading type information..."))); import->valid = false; } } else if (libraryInfo.pluginTypeInfoStatus() == LibraryInfo::DumpError diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.cpp b/src/libs/qmljs/qmljsmodelmanagerinterface.cpp index d88b1c261e..65a47b2bd9 100644 --- a/src/libs/qmljs/qmljsmodelmanagerinterface.cpp +++ b/src/libs/qmljs/qmljsmodelmanagerinterface.cpp @@ -782,7 +782,8 @@ static bool findNewQmlLibraryInPath(const QString &path, } // found a new library! - qmldirFile.open(QFile::ReadOnly); + if (!qmldirFile.open(QFile::ReadOnly)) + return false; QString qmldirData = QString::fromUtf8(qmldirFile.readAll()); QmlDirParser qmldirParser; diff --git a/src/libs/qmljs/qmljsreformatter.cpp b/src/libs/qmljs/qmljsreformatter.cpp index ab43c03706..6c4befa0f8 100644 --- a/src/libs/qmljs/qmljsreformatter.cpp +++ b/src/libs/qmljs/qmljsreformatter.cpp @@ -150,8 +150,12 @@ protected: void outCommentText(const QString &str) { QStringList lines = str.split(QLatin1Char('\n')); + bool multiline = lines.length() > 1; for (int i = 0; i < lines.size(); ++i) { - _line = lines.at(i); // multiline comments don't keep track of previos lines + if (multiline) + _line = lines.at(i); // multiline comments don't keep track of previos lines + else + _line += lines.at(i); if (i != lines.size() - 1) newLine(); } @@ -582,7 +586,7 @@ protected: out(ast->identifierToken); } } else { // signal - out("signal "); + out("signal ", ast->identifierToken); out(ast->identifierToken); if (ast->parameters) { out("("); diff --git a/src/libs/qmljs/qmljstypedescriptionreader.cpp b/src/libs/qmljs/qmljstypedescriptionreader.cpp index 4a7f30afc4..8c90b416d7 100644 --- a/src/libs/qmljs/qmljstypedescriptionreader.cpp +++ b/src/libs/qmljs/qmljstypedescriptionreader.cpp @@ -641,7 +641,9 @@ void TypeDescriptionReader::readMetaObjectRevisions(UiScriptBinding *ast, FakeMe void TypeDescriptionReader::readEnumValues(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaEnum *fme) { - if (!ast || !ast->statement) { + if (!ast) + return; + if (!ast->statement) { addError(ast->colonToken, tr("Expected object literal after colon.")); return; } diff --git a/src/libs/qtcreatorcdbext/pycdbextmodule.cpp b/src/libs/qtcreatorcdbext/pycdbextmodule.cpp index 4ca11a777d..2b807d18ac 100644 --- a/src/libs/qtcreatorcdbext/pycdbextmodule.cpp +++ b/src/libs/qtcreatorcdbext/pycdbextmodule.cpp @@ -40,6 +40,7 @@ #include <iterator> static CurrentSymbolGroup currentSymbolGroup; +static std::string results; CurrentSymbolGroup::~CurrentSymbolGroup() { @@ -338,6 +339,16 @@ static PyObject *cdbext_call(PyObject *, PyObject *args) return createPythonObject(PyValue(index, symbolGroup)); } +static PyObject *cdbext_reportResult(PyObject *, PyObject *args) +{ + char *result; + if (!PyArg_ParseTuple(args, "s", &result)) + Py_RETURN_NONE; + + results += result; + Py_RETURN_NONE; +} + static PyMethodDef cdbextMethods[] = { {"parseAndEvaluate", cdbext_parseAndEvaluate, METH_VARARGS, "Returns value of expression or None if the expression can not be resolved"}, @@ -361,6 +372,8 @@ static PyMethodDef cdbextMethods[] = { "Creates a value with the given type at the given address"}, {"call", cdbext_call, METH_VARARGS, "Call a function and return a cdbext.Value representing the return value of that function."}, + {"reportResult", cdbext_reportResult, METH_VARARGS, + "Adds a result"}, {NULL, NULL, 0, NULL} /* Sentinel */ }; @@ -420,3 +433,25 @@ int pointerSize() { return ExtensionCommandContext::instance()->control()->IsPointer64Bit() == S_OK ? 8 : 4; } + +std::string collectOutput() +{ + // construct a gdbmi output string with two children: messages and result + std::stringstream ret; + ret << "output=[msg=["; + std::istringstream pyStdout(getPyStdout()); + std::string line; + // Add a child to messages for every line. + while (std::getline(pyStdout, line)) { + // there are two kinds of messages we want to handle here: + if (line.find("bridgemessage=") == 0) { // preformatted gdmi bridgemessages from warn() + ret << line << ','; + } else { // and a line of "normal" python output + replace(line, '"', '$'); // otherwise creators gdbmi parser would fail + ret << "line=\"" << line << "\","; + } + } + ret << "]," << results << "]"; + results.clear(); + return ret.str(); +} diff --git a/src/libs/qtcreatorcdbext/pycdbextmodule.h b/src/libs/qtcreatorcdbext/pycdbextmodule.h index f1bc7d4c63..91c05bf12d 100644 --- a/src/libs/qtcreatorcdbext/pycdbextmodule.h +++ b/src/libs/qtcreatorcdbext/pycdbextmodule.h @@ -27,11 +27,13 @@ #include <Python.h> #include <vector> +#include <string> #include "dbgeng.h" void initCdbextPythonModule(); int pointerSize(); +std::string collectOutput(); constexpr bool debugPyCdbextModule = false; using Bytes = std::vector<char>; diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp index 63e79777fd..f98205db72 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp @@ -34,6 +34,7 @@ #ifdef WITH_PYTHON #include <Python.h> #include "pystdoutredirect.h" +#include "pycdbextmodule.h" #endif #include <cstdio> @@ -588,7 +589,7 @@ extern "C" HRESULT CALLBACK script(CIDebugClient *client, PCSTR argsIn) const char result = (PyRun_SimpleString(command.str().c_str()) == 0) ? 'R' : 'N'; if (PyErr_Occurred()) PyErr_Print(); - ExtensionContext::instance().reportLong(result, token, "script", getPyStdout().c_str()); + ExtensionContext::instance().reportLong(result, token, "script", collectOutput().c_str()); endCapturePyStdout(); PyErr_Restore(ptype, pvalue, ptraceback); #else @@ -849,10 +850,15 @@ extern "C" HRESULT CALLBACK assign(CIDebugClient *client, PCSTR argsIn) const std::string iname = tokens.front().substr(0, equalsPos); const std::string value = tokens.front().substr(equalsPos + 1, tokens.front().size() - equalsPos - 1); // get the symbolgroup - const int currentFrame = ExtensionContext::instance().symbolGroupFrame(); + int currentFrame = ExtensionContext::instance().symbolGroupFrame(); if (currentFrame < 0) { - errorMessage = "No current frame."; - break; + CIDebugControl *control = ExtensionCommandContext::instance()->control(); + DEBUG_STACK_FRAME frame; + if (FAILED(control->GetStackTrace(0, 0, 0, &frame, 1, NULL))) { + errorMessage = "No current frame."; + break; + } + currentFrame = frame.FrameNumber; } SymbolGroup *symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), currentFrame, &errorMessage); if (!symGroup) diff --git a/src/libs/qtcreatorcdbext/symbolgroup.cpp b/src/libs/qtcreatorcdbext/symbolgroup.cpp index ed9cb0f248..5ea3e8930e 100644 --- a/src/libs/qtcreatorcdbext/symbolgroup.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroup.cpp @@ -465,8 +465,11 @@ bool SymbolGroup::assign(const std::string &nodeName, return false; } - return (node->dumperType() & KT_Editable) ? // Edit complex types - assignType(node, valueEncoding, value, ctx, errorMessage) : + int kt = node->dumperType(); + if (kt < 0) + kt = knownType(node->type(), KnownTypeAutoStripPointer | KnownTypeHasClassPrefix); + return (kt & KT_Editable) ? // Edit complex types + assignType(node, kt, valueEncoding, value, ctx, errorMessage) : node->assign(value, errorMessage); } diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp index 9c5ff7bc25..d27e6bdf1f 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp @@ -3552,17 +3552,17 @@ static inline bool assignStdString(SymbolGroupNode *n, return true; } -bool assignType(SymbolGroupNode *n, int valueEncoding, const std::string &value, +bool assignType(SymbolGroupNode *n, int knownType, int valueEncoding, const std::string &value, const SymbolGroupValueContext &ctx, std::string *errorMessage) { - switch (n->dumperType()) { + switch (knownType) { case KT_QString: return assignQString(n, valueEncoding, value, ctx, errorMessage); case KT_QByteArray: return assignQByteArray(n, valueEncoding, value, ctx, errorMessage); case KT_StdString: case KT_StdWString: - return assignStdString(n, n->dumperType(), valueEncoding, value, ctx, errorMessage); + return assignStdString(n, knownType, valueEncoding, value, ctx, errorMessage); default: break; } diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.h b/src/libs/qtcreatorcdbext/symbolgroupvalue.h index 61c5a67523..b2c89cba47 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupvalue.h +++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.h @@ -286,7 +286,7 @@ enum AssignEncoding AssignHexEncodedUtf16 }; -bool assignType(SymbolGroupNode *n, int valueEncoding, const std::string &value, +bool assignType(SymbolGroupNode *n, int knownType, int valueEncoding, const std::string &value, const SymbolGroupValueContext &ctx, std::string *errorMessage); diff --git a/src/libs/timeline/qml/MainView.qml b/src/libs/timeline/qml/MainView.qml index 5a7acac4d0..e6aa2bb15e 100644 --- a/src/libs/timeline/qml/MainView.qml +++ b/src/libs/timeline/qml/MainView.qml @@ -92,7 +92,7 @@ Rectangle { // This is called from outside to synchronize the timeline to other views function selectByTypeId(typeId) { - if (lockItemSelection || typeId === -1) + if (lockItemSelection || typeId === -1 || content.typeId === typeId) return; var itemIndex = -1; diff --git a/src/libs/utils/appmainwindow.cpp b/src/libs/utils/appmainwindow.cpp index 7c9a8afe1f..bdeb3a4cbf 100644 --- a/src/libs/utils/appmainwindow.cpp +++ b/src/libs/utils/appmainwindow.cpp @@ -24,6 +24,7 @@ ****************************************************************************/ #include "appmainwindow.h" +#include "theme/theme_p.h" #ifdef Q_OS_WIN #include <windows.h> @@ -60,11 +61,14 @@ void AppMainWindow::raiseWindow() #ifdef Q_OS_WIN bool AppMainWindow::event(QEvent *event) { - if (event->type() == m_deviceEventId) { + const QEvent::Type type = event->type(); + if (type == m_deviceEventId) { event->accept(); emit deviceChange(); return true; } + if (type == QEvent::ThemeChange) + setThemeApplicationPalette(); return QMainWindow::event(event); } diff --git a/src/libs/utils/completinglineedit.cpp b/src/libs/utils/completinglineedit.cpp index 52d2c5d7f1..5ae7448288 100644 --- a/src/libs/utils/completinglineedit.cpp +++ b/src/libs/utils/completinglineedit.cpp @@ -62,7 +62,7 @@ void CompletingLineEdit::keyPressEvent(QKeyEvent *e) comp->complete(); } } - return QLineEdit::keyPressEvent(e); + QLineEdit::keyPressEvent(e); } } // namespace Utils diff --git a/src/libs/utils/consoleprocess_unix.cpp b/src/libs/utils/consoleprocess_unix.cpp index 192f340940..29e3d1845c 100644 --- a/src/libs/utils/consoleprocess_unix.cpp +++ b/src/libs/utils/consoleprocess_unix.cpp @@ -31,6 +31,7 @@ #include <utils/qtcassert.h> #include <QCoreApplication> +#include <QFileInfo> #include <QSettings> #include <QTimer> @@ -359,7 +360,7 @@ QString ConsoleProcess::defaultTerminalEmulator() { if (HostOsInfo::isMacHost()) { QString termCmd = QCoreApplication::applicationDirPath() + QLatin1String("/../Resources/scripts/openTerminal.command"); - if (QFile(termCmd).exists()) + if (QFileInfo::exists(termCmd)) return termCmd.replace(QLatin1Char(' '), QLatin1String("\\ ")); return QLatin1String("/usr/X11/bin/xterm"); } @@ -407,7 +408,7 @@ QString ConsoleProcess::terminalEmulator(const QSettings *settings, bool nonEmpt void ConsoleProcess::setTerminalEmulator(QSettings *settings, const QString &term) { - return settings->setValue(QLatin1String("General/TerminalEmulator"), term); + settings->setValue(QLatin1String("General/TerminalEmulator"), term); } bool ConsoleProcess::startTerminalEmulator(QSettings *settings, const QString &workingDir) diff --git a/src/libs/utils/crumblepath.cpp b/src/libs/utils/crumblepath.cpp index 2d3e7bbb78..e32e344b96 100644 --- a/src/libs/utils/crumblepath.cpp +++ b/src/libs/utils/crumblepath.cpp @@ -365,6 +365,7 @@ void CrumblePath::resizeButtons() // compute relative sizes QList<int> sizes; int totalSize = 0; + sizes.reserve(m_buttons.length()); for (int i = 0; i < m_buttons.length() ; ++i) { CrumblePathButton *button = m_buttons.at(i); diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp index bc20f85efc..bb79efe8db 100644 --- a/src/libs/utils/pathchooser.cpp +++ b/src/libs/utils/pathchooser.cpp @@ -56,7 +56,7 @@ static QString appBundleExpandedPath(const QString &path) QFileInfo info(path); if (info.isDir()) { QString exePath = path + QLatin1String("/Contents/MacOS/") + info.completeBaseName(); - if (QFileInfo(exePath).exists()) + if (QFileInfo::exists(exePath)) return exePath; } } diff --git a/src/libs/utils/theme/theme.cpp b/src/libs/utils/theme/theme.cpp index b8147de046..061ff335cc 100644 --- a/src/libs/utils/theme/theme.cpp +++ b/src/libs/utils/theme/theme.cpp @@ -57,14 +57,19 @@ Theme *proxyTheme() return new Theme(m_creatorTheme); } +void setThemeApplicationPalette() +{ + if (m_creatorTheme && m_creatorTheme->flag(Theme::ApplyThemePaletteGlobally)) + QApplication::setPalette(m_creatorTheme->palette()); +} + void setCreatorTheme(Theme *theme) { if (m_creatorTheme == theme) return; delete m_creatorTheme; m_creatorTheme = theme; - if (theme && theme->flag(Theme::ApplyThemePaletteGlobally)) - QApplication::setPalette(theme->palette()); + setThemeApplicationPalette(); } Theme::Theme(const QString &id, QObject *parent) diff --git a/src/libs/utils/theme/theme_p.h b/src/libs/utils/theme/theme_p.h index 4170ec7cd9..1feeeda4e2 100644 --- a/src/libs/utils/theme/theme_p.h +++ b/src/libs/utils/theme/theme_p.h @@ -51,5 +51,6 @@ public: }; QTCREATOR_UTILS_EXPORT void setCreatorTheme(Theme *theme); +QTCREATOR_UTILS_EXPORT void setThemeApplicationPalette(); } // namespace Utils diff --git a/src/libs/utils/treemodel.cpp b/src/libs/utils/treemodel.cpp index 5281ab33f5..db8923e372 100644 --- a/src/libs/utils/treemodel.cpp +++ b/src/libs/utils/treemodel.cpp @@ -919,11 +919,9 @@ QModelIndex BaseTreeModel::parent(const QModelIndex &idx) const if (!grandparent) return QModelIndex(); - for (int i = 0, n = grandparent->childCount(); i < n; ++i) - if (grandparent->childAt(i) == parent) - return createIndex(i, 0, static_cast<void*>(parent)); - - return QModelIndex(); + // This is on the performance-critical path for ItemViewFind. + const int i = grandparent->m_children.indexOf(parent); + return createIndex(i, 0, static_cast<void*>(parent)); } int BaseTreeModel::rowCount(const QModelIndex &idx) const diff --git a/src/libs/utils/treemodel.h b/src/libs/utils/treemodel.h index 8782d7b37d..60db234d3b 100644 --- a/src/libs/utils/treemodel.h +++ b/src/libs/utils/treemodel.h @@ -66,7 +66,7 @@ public: using const_iterator = QVector<TreeItem *>::const_iterator; using value_type = TreeItem *; - int childCount() const { return end() - begin(); } + int childCount() const { return m_children.size(); } int indexInParent() const; TreeItem *childAt(int index) const; int indexOf(const TreeItem *item) const; diff --git a/src/libs/utils/unixutils.cpp b/src/libs/utils/unixutils.cpp index f08ee196c8..7db05c22ea 100644 --- a/src/libs/utils/unixutils.cpp +++ b/src/libs/utils/unixutils.cpp @@ -47,7 +47,7 @@ QString UnixUtils::fileBrowser(const QSettings *settings) void UnixUtils::setFileBrowser(QSettings *settings, const QString &term) { - return settings->setValue(QLatin1String("General/FileBrowser"), term); + settings->setValue(QLatin1String("General/FileBrowser"), term); } diff --git a/src/plugins/android/android.pro b/src/plugins/android/android.pro index 0df9a4a4ae..3d63124af5 100644 --- a/src/plugins/android/android.pro +++ b/src/plugins/android/android.pro @@ -47,7 +47,10 @@ HEADERS += \ android_global.h \ androidbuildapkstep.h \ androidbuildapkwidget.h \ - androidrunnable.h + androidrunnable.h \ + androidtoolmanager.h \ + androidsdkmanager.h \ + androidavdmanager.h SOURCES += \ androidconfigurations.cpp \ @@ -88,7 +91,10 @@ SOURCES += \ androidbuildapkstep.cpp \ androidbuildapkwidget.cpp \ androidqtsupport.cpp \ - androidrunnable.cpp + androidrunnable.cpp \ + androidtoolmanager.cpp \ + androidsdkmanager.cpp \ + androidavdmanager.cpp FORMS += \ androidsettingswidget.ui \ diff --git a/src/plugins/android/android.qbs b/src/plugins/android/android.qbs index 1bb3296453..c3a2551bae 100644 --- a/src/plugins/android/android.qbs +++ b/src/plugins/android/android.qbs @@ -22,6 +22,8 @@ Project { "android.qrc", "androidanalyzesupport.cpp", "androidanalyzesupport.h", + "androidavdmanager.cpp", + "androidavdmanager.h", "androidconfigurations.cpp", "androidconfigurations.h", "androidconstants.h", @@ -84,6 +86,8 @@ Project { "androidrunnable.h", "androidrunner.cpp", "androidrunner.h", + "androidsdkmanager.cpp", + "androidsdkmanager.h", "androidsettingspage.cpp", "androidsettingspage.h", "androidsettingswidget.cpp", @@ -93,6 +97,8 @@ Project { "androidsignaloperation.h", "androidtoolchain.cpp", "androidtoolchain.h", + "androidtoolmanager.cpp", + "androidtoolmanager.h", "avddialog.cpp", "avddialog.h", "certificatesmodel.cpp", diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp new file mode 100644 index 0000000000..028fe2c797 --- /dev/null +++ b/src/plugins/android/androidavdmanager.cpp @@ -0,0 +1,441 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#include "androidavdmanager.h" + +#include "androidtoolmanager.h" + +#include "utils/algorithm.h" +#include "utils/qtcassert.h" +#include "utils/runextensions.h" +#include "utils/synchronousprocess.h" + +#include <QApplication> +#include <QFileInfo> +#include <QLoggingCategory> +#include <QSettings> + +#include <chrono> + +namespace { +Q_LOGGING_CATEGORY(avdManagerLog, "qtc.android.avdManager") +} + +namespace Android { +namespace Internal { + +using namespace std; + +// Avd list keys to parse avd +const char avdInfoNameKey[] = "Name:"; +const char avdInfoPathKey[] = "Path:"; +const char avdInfoAbiKey[] = "abi.type"; +const char avdInfoTargetKey[] = "target"; +const char avdInfoErrorKey[] = "Error:"; + +const QVersionNumber avdManagerIntroVersion(25, 3 ,0); + +const int avdCreateTimeoutMs = 30000; + +/*! + Runs the \c avdmanager tool specific to configuration \a config with arguments \a args. Returns + \c true if the command is successfully executed. Output is copied into \a output. The function + blocks the calling thread. + */ +static bool avdManagerCommand(const AndroidConfig config, const QStringList &args, QString *output) +{ + QString avdManagerToolPath = config.avdManagerToolPath().toString(); + Utils::SynchronousProcess proc; + Utils::SynchronousProcessResponse response = proc.runBlocking(avdManagerToolPath, args); + if (response.result == Utils::SynchronousProcessResponse::Finished) { + if (output) + *output = response.allOutput(); + return true; + } + return false; +} + +/*! + Parses the \a line for a [spaces]key[spaces]value[spaces] pattern and returns + \c true if the key is found, \c false otherwise. The value is copied into \a value. + */ +static bool valueForKey(QString key, const QString &line, QString *value = nullptr) +{ + auto trimmedInput = line.trimmed(); + if (trimmedInput.startsWith(key)) { + if (value) + *value = trimmedInput.section(key, 1, 1).trimmed(); + return true; + } + return false; +} + +static bool checkForTimeout(const chrono::steady_clock::time_point &start, + int msecs = 3000) +{ + bool timedOut = false; + auto end = chrono::steady_clock::now(); + if (chrono::duration_cast<chrono::milliseconds>(end-start).count() > msecs) + timedOut = true; + return timedOut; +} + +static AndroidConfig::CreateAvdInfo createAvdCommand(const AndroidConfig config, + const AndroidConfig::CreateAvdInfo &info) +{ + AndroidConfig::CreateAvdInfo result = info; + + if (!result.isValid()) { + qCDebug(avdManagerLog) << "AVD Create failed. Invalid CreateAvdInfo" << result.name + << result.target.name << result.target.apiLevel; + result.error = QApplication::translate("AndroidAvdManager", + "Cannot create AVD. Invalid input."); + return result; + } + + QStringList arguments({"create", "avd", "-k", result.target.package, "-n", result.name}); + + if (!result.abi.isEmpty()) { + SystemImage image = Utils::findOrDefault(result.target.systemImages, + Utils::equal(&SystemImage::abiName, result.abi)); + if (image.isValid()) { + arguments << "-k" << image.package; + } else { + qCDebug(avdManagerLog) << "AVD Create failed. Cannot find system image for the platform" + << result.abi << result.target.name; + result.error = QApplication::translate("AndroidAvdManager", + "Cannot create AVD. Cannot find system image for " + "the ABI %1(%2).").arg(result.abi).arg(result.target.name); + return result; + } + + } else { + arguments << "-k" << result.target.package; + } + + if (result.sdcardSize > 0) + arguments << "-c" << QString::fromLatin1("%1M").arg(result.sdcardSize); + + QProcess proc; + proc.start(config.avdManagerToolPath().toString(), arguments); + if (!proc.waitForStarted()) { + result.error = QApplication::translate("AndroidAvdManager", + "Could not start process \"%1 %2\"") + .arg(config.avdManagerToolPath().toString(), arguments.join(' ')); + return result; + } + QTC_CHECK(proc.state() == QProcess::Running); + proc.write(QByteArray("yes\n")); // yes to "Do you wish to create a custom hardware profile" + + auto start = chrono::steady_clock::now(); + QString errorOutput; + QByteArray question; + while (errorOutput.isEmpty()) { + proc.waitForReadyRead(500); + question += proc.readAllStandardOutput(); + if (question.endsWith(QByteArray("]:"))) { + // truncate to last line + int index = question.lastIndexOf(QByteArray("\n")); + if (index != -1) + question = question.mid(index); + if (question.contains("hw.gpu.enabled")) + proc.write(QByteArray("yes\n")); + else + proc.write(QByteArray("\n")); + question.clear(); + } + // The exit code is always 0, so we need to check stderr + // For now assume that any output at all indicates a error + errorOutput = QString::fromLocal8Bit(proc.readAllStandardError()); + if (proc.state() != QProcess::Running) + break; + + // For a sane input and command, process should finish before timeout. + if (checkForTimeout(start, avdCreateTimeoutMs)) { + result.error = QApplication::translate("AndroidAvdManager", + "Cannot create AVD. Command timed out."); + } + } + + // Kill the running process. + if (proc.state() != QProcess::NotRunning) { + proc.terminate(); + if (!proc.waitForFinished(3000)) + proc.kill(); + } + + QTC_CHECK(proc.state() == QProcess::NotRunning); + result.error = errorOutput; + return result; +} + +/*! + \class AvdManagerOutputParser + \brief The AvdManagerOutputParser class is a helper class to parse the output of the avdmanager + commands. + */ +class AvdManagerOutputParser +{ +public: + AndroidDeviceInfoList listVirtualDevices(const AndroidConfig &config); + AndroidDeviceInfoList parseAvdList(const QString &output); + +private: + bool parseAvd(const QStringList &deviceInfo, AndroidDeviceInfo *avd); +}; + + +AndroidAvdManager::AndroidAvdManager(const AndroidConfig &config): + m_config(config), + m_androidTool(new AndroidToolManager(m_config)), + m_parser(new AvdManagerOutputParser) +{ + +} + +AndroidAvdManager::~AndroidAvdManager() +{ + +} + +bool AndroidAvdManager::avdManagerUiToolAvailable() const +{ + return m_config.sdkToolsVersion() < avdManagerIntroVersion; +} + +void AndroidAvdManager::launchAvdManagerUiTool() const +{ + if (avdManagerUiToolAvailable()) { + m_androidTool->launchAvdManager(); + } else { + qCDebug(avdManagerLog) << "AVD Ui tool launch failed. UI tool not available" + << m_config.sdkToolsVersion(); + } +} + +QFuture<AndroidConfig::CreateAvdInfo> +AndroidAvdManager::createAvd(AndroidConfig::CreateAvdInfo info) const +{ + if (m_config.sdkToolsVersion() < avdManagerIntroVersion) + return m_androidTool->createAvd(info); + + return Utils::runAsync(&createAvdCommand, m_config, info); +} + +bool AndroidAvdManager::removeAvd(const QString &name) const +{ + if (m_config.sdkToolsVersion() < avdManagerIntroVersion) + return m_androidTool->removeAvd(name); + + Utils::SynchronousProcess proc; + proc.setTimeoutS(5); + Utils::SynchronousProcessResponse response + = proc.runBlocking(m_config.avdManagerToolPath().toString(), + QStringList({"delete", "avd", "-n", name})); + return response.result == Utils::SynchronousProcessResponse::Finished && response.exitCode == 0; +} + +QFuture<AndroidDeviceInfoList> AndroidAvdManager::avdList() const +{ + if (m_config.sdkToolsVersion() < avdManagerIntroVersion) + return m_androidTool->androidVirtualDevicesFuture(); + + return Utils::runAsync(&AvdManagerOutputParser::listVirtualDevices, m_parser.get(), m_config); +} + +QString AndroidAvdManager::startAvd(const QString &name) const +{ + if (!findAvd(name).isEmpty() || startAvdAsync(name)) + return waitForAvd(name); + return QString(); +} + +bool AndroidAvdManager::startAvdAsync(const QString &avdName) const +{ + QProcess *avdProcess = new QProcess(); + QObject::connect(avdProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished), + avdProcess, &QObject::deleteLater); + + // start the emulator + QStringList arguments; + if (AndroidConfigurations::force32bitEmulator()) + arguments << "-force-32bit"; + + arguments << "-partition-size" << QString::number(m_config.partitionSize()) + << "-avd" << avdName; + avdProcess->start(m_config.emulatorToolPath().toString(), arguments); + if (!avdProcess->waitForStarted(-1)) { + delete avdProcess; + return false; + } + return true; +} + +QString AndroidAvdManager::findAvd(const QString &avdName) const +{ + QVector<AndroidDeviceInfo> devices = m_config.connectedDevices(); + foreach (AndroidDeviceInfo device, devices) { + if (device.type != AndroidDeviceInfo::Emulator) + continue; + if (device.avdname == avdName) + return device.serialNumber; + } + return QString(); +} + +QString AndroidAvdManager::waitForAvd(const QString &avdName, const QFutureInterface<bool> &fi) const +{ + // we cannot use adb -e wait-for-device, since that doesn't work if a emulator is already running + // 60 rounds of 2s sleeping, two minutes for the avd to start + QString serialNumber; + for (int i = 0; i < 60; ++i) { + if (fi.isCanceled()) + return QString(); + serialNumber = findAvd(avdName); + if (!serialNumber.isEmpty()) + return waitForBooted(serialNumber, fi) ? serialNumber : QString(); + QThread::sleep(2); + } + return QString(); +} + +bool AndroidAvdManager::isAvdBooted(const QString &device) const +{ + QStringList arguments = AndroidDeviceInfo::adbSelector(device); + arguments << "shell" << "getprop" << "init.svc.bootanim"; + + Utils::SynchronousProcess adbProc; + adbProc.setTimeoutS(10); + Utils::SynchronousProcessResponse response = + adbProc.runBlocking(m_config.adbToolPath().toString(), arguments); + if (response.result != Utils::SynchronousProcessResponse::Finished) + return false; + QString value = response.allOutput().trimmed(); + return value == "stopped"; +} + +bool AndroidAvdManager::waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const +{ + // found a serial number, now wait until it's done booting... + for (int i = 0; i < 60; ++i) { + if (fi.isCanceled()) + return false; + if (isAvdBooted(serialNumber)) { + return true; + } else { + QThread::sleep(2); + if (!m_config.isConnected(serialNumber)) // device was disconnected + return false; + } + } + return false; +} + +AndroidDeviceInfoList AvdManagerOutputParser::listVirtualDevices(const AndroidConfig &config) +{ + QString output; + if (!avdManagerCommand(config, QStringList({"list", "avd"}), &output)) { + qCDebug(avdManagerLog) << "Avd list command failed" << output << config.sdkToolsVersion(); + return {}; + } + return parseAvdList(output); +} + +AndroidDeviceInfoList AvdManagerOutputParser::parseAvdList(const QString &output) +{ + AndroidDeviceInfoList avdList; + QStringList avdInfo; + auto parseAvdInfo = [&avdInfo, &avdList, this] () { + AndroidDeviceInfo avd; + if (parseAvd(avdInfo, &avd)) { + // armeabi-v7a devices can also run armeabi code + if (avd.cpuAbi.contains("armeabi-v7a")) + avd.cpuAbi << "armeabi"; + avd.state = AndroidDeviceInfo::OkState; + avd.type = AndroidDeviceInfo::Emulator; + avdList << avd; + } else { + qCDebug(avdManagerLog) << "Avd Parsing: Parsing failed: " << avdInfo; + } + avdInfo.clear(); + }; + + foreach (QString line, output.split('\n')) { + if (line.startsWith("---------") || line.isEmpty()) { + parseAvdInfo(); + } else { + avdInfo << line; + } + } + + if (!avdInfo.isEmpty()) + parseAvdInfo(); + + Utils::sort(avdList); + + return avdList; +} + +bool AvdManagerOutputParser::parseAvd(const QStringList &deviceInfo, AndroidDeviceInfo *avd) +{ + QTC_ASSERT(avd, return false); + foreach (const QString &line, deviceInfo) { + QString value; + if (valueForKey(avdInfoErrorKey, line)) { + qCDebug(avdManagerLog) << "Avd Parsing: Skip avd device. Error key found:" << line; + return false; + } else if (valueForKey(avdInfoNameKey, line, &value)) { + avd->avdname = value; + } else if (valueForKey(avdInfoPathKey, line, &value)) { + const Utils::FileName avdPath = Utils::FileName::fromString(value); + if (avdPath.exists()) + { + // Get ABI. + Utils::FileName configFile = avdPath; + configFile.appendPath("config.ini"); + QSettings config(configFile.toString(), QSettings::IniFormat); + value = config.value(avdInfoAbiKey).toString(); + if (!value.isEmpty()) + avd->cpuAbi << value; + else + qCDebug(avdManagerLog) << "Avd Parsing: Cannot find ABI:" << configFile; + + // Get Target + Utils::FileName avdInfoFile = avdPath.parentDir(); + QString avdInfoFileName = avdPath.toFileInfo().baseName() + ".ini"; + avdInfoFile.appendPath(avdInfoFileName); + QSettings avdInfo(avdInfoFile.toString(), QSettings::IniFormat); + value = avdInfo.value(avdInfoTargetKey).toString(); + if (!value.isEmpty()) + avd->sdk = value.section('-', -1).toInt(); + else + qCDebug(avdManagerLog) << "Avd Parsing: Cannot find sdk API:" << avdInfoFile.toString(); + } + } + } + return true; +} + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidavdmanager.h b/src/plugins/android/androidavdmanager.h new file mode 100644 index 0000000000..4e8633efda --- /dev/null +++ b/src/plugins/android/androidavdmanager.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#pragma once + +#include "androidconfigurations.h" + +#include <memory> + +namespace Android { +namespace Internal { + +class AndroidToolManager; +class AvdManagerOutputParser; + +class AndroidAvdManager +{ +public: + AndroidAvdManager(const AndroidConfig& config = AndroidConfigurations::currentConfig()); + ~AndroidAvdManager(); + + bool avdManagerUiToolAvailable() const; + void launchAvdManagerUiTool() const; + QFuture<AndroidConfig::CreateAvdInfo> createAvd(AndroidConfig::CreateAvdInfo info) const; + bool removeAvd(const QString &name) const; + QFuture<AndroidDeviceInfoList> avdList() const; + + QString startAvd(const QString &name) const; + bool startAvdAsync(const QString &avdName) const; + QString findAvd(const QString &avdName) const; + QString waitForAvd(const QString &avdName, + const QFutureInterface<bool> &fi = QFutureInterface<bool>()) const; + bool isAvdBooted(const QString &device) const; + +private: + bool waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const; + +private: + const AndroidConfig &m_config; + std::unique_ptr<AndroidToolManager> m_androidTool; + std::unique_ptr<AvdManagerOutputParser> m_parser; +}; + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp index 0ff998809f..9966b21faf 100644 --- a/src/plugins/android/androidbuildapkstep.cpp +++ b/src/plugins/android/androidbuildapkstep.cpp @@ -60,6 +60,8 @@ namespace Android { using namespace Internal; +const QVersionNumber gradleScriptRevokedSdkVersion(25, 3, 0); +const QVersionNumber gradleScriptsContainedQtVersion(5, 9, 0); const QLatin1String DeployActionKey("Qt4ProjectManager.AndroidDeployQtStep.DeployQtAction"); const QLatin1String KeystoreLocationKey("KeystoreLocation"); const QLatin1String BuildTargetSdkKey("BuildTargetSdk"); @@ -140,6 +142,15 @@ bool AndroidBuildApkStep::init(QList<const BuildStep *> &earlierSteps) if (!version) return false; + if (AndroidConfigurations::currentConfig().sdkToolsVersion() >= gradleScriptRevokedSdkVersion && + QVersionNumber::fromString(version->qtVersionString()) < gradleScriptsContainedQtVersion) { + emit addOutput(tr("The installed SDK tools version (%1) does not include Gradle scripts. The " + "minimum Qt version required for Gradle build to work is %2") + .arg(gradleScriptRevokedSdkVersion.toString()) + .arg(gradleScriptsContainedQtVersion.toString()), OutputFormat::Stderr); + return false; + } + int minSDKForKit = AndroidManager::minimumSDK(target()->kit()); if (AndroidManager::minimumSDK(target()) < minSDKForKit) { emit addOutput(tr("The API level set for the APK is less than the minimum required by the kit." @@ -342,6 +353,16 @@ void AndroidBuildApkStep::setUseGradle(bool b) } } +bool AndroidBuildApkStep::addDebugger() const +{ + return m_addDebugger; +} + +void AndroidBuildApkStep::setAddDebugger(bool debug) +{ + m_addDebugger = debug; +} + bool AndroidBuildApkStep::verboseOutput() const { return m_verbose; diff --git a/src/plugins/android/androidbuildapkstep.h b/src/plugins/android/androidbuildapkstep.h index 0d044c0ccc..eae827eb15 100644 --- a/src/plugins/android/androidbuildapkstep.h +++ b/src/plugins/android/androidbuildapkstep.h @@ -73,6 +73,9 @@ public: bool useGradle() const; void setUseGradle(bool b); + bool addDebugger() const; + void setAddDebugger(bool debug); + QString buildTargetSdk() const; void setBuildTargetSdk(const QString &sdk); @@ -99,9 +102,10 @@ protected: AndroidDeployAction m_deployAction = BundleLibrariesDeployment; bool m_signPackage = false; bool m_verbose = false; - bool m_useGradle = false; + bool m_useGradle = true; // Ant builds are deprecated. bool m_openPackageLocation = false; bool m_openPackageLocationForRun = false; + bool m_addDebugger = true; QString m_buildTargetSdk; Utils::FileName m_keystorePath; diff --git a/src/plugins/android/androidbuildapkwidget.cpp b/src/plugins/android/androidbuildapkwidget.cpp index 2e85c243fc..97dd4695bd 100644 --- a/src/plugins/android/androidbuildapkwidget.cpp +++ b/src/plugins/android/androidbuildapkwidget.cpp @@ -54,9 +54,12 @@ AndroidBuildApkWidget::AndroidBuildApkWidget(AndroidBuildApkStep *step) { m_ui->setupUi(this); + m_ui->deprecatedInfoIconLabel->setPixmap(Utils::Icons::INFO.pixmap()); + // Target sdk combobox int minApiLevel = 9; - QStringList targets = AndroidConfig::apiLevelNamesFor(AndroidConfigurations::currentConfig().sdkTargets(minApiLevel)); + const AndroidConfig &config = AndroidConfigurations::currentConfig(); + QStringList targets = AndroidConfig::apiLevelNamesFor(config.sdkTargets(minApiLevel)); targets.removeDuplicates(); m_ui->targetSDKComboBox->addItems(targets); m_ui->targetSDKComboBox->setCurrentIndex(targets.indexOf(AndroidManager::buildTargetSDK(step->target()))); @@ -91,9 +94,12 @@ AndroidBuildApkWidget::AndroidBuildApkWidget(AndroidBuildApkStep *step) m_ui->signingDebugDeployErrorIcon->setPixmap(Utils::Icons::CRITICAL.pixmap()); signPackageCheckBoxToggled(m_step->signPackage()); - m_ui->useGradleCheckBox->setChecked(m_step->useGradle()); + m_ui->useGradleCheckBox->setEnabled(config.antScriptsAvailable()); + m_ui->useGradleCheckBox->setChecked(!config.antScriptsAvailable() || + m_step->useGradle()); m_ui->verboseOutputCheckBox->setChecked(m_step->verboseOutput()); m_ui->openPackageLocationCheckBox->setChecked(m_step->openPackageLocation()); + m_ui->addDebuggerCheckBox->setChecked(m_step->addDebugger()); // target sdk connect(m_ui->targetSDKComboBox, @@ -120,6 +126,8 @@ AndroidBuildApkWidget::AndroidBuildApkWidget(AndroidBuildApkStep *step) this, &AndroidBuildApkWidget::openPackageLocationCheckBoxToggled); connect(m_ui->verboseOutputCheckBox, &QAbstractButton::toggled, this, &AndroidBuildApkWidget::verboseOutputCheckBoxToggled); + connect(m_ui->addDebuggerCheckBox, &QAbstractButton::toggled, + m_step, &AndroidBuildApkStep::setAddDebugger); //signing connect(m_ui->signPackageCheckBox, &QAbstractButton::toggled, @@ -185,6 +193,7 @@ void AndroidBuildApkWidget::signPackageCheckBoxToggled(bool checked) { m_ui->certificatesAliasComboBox->setEnabled(checked); m_step->setSignPackage(checked); + m_ui->addDebuggerCheckBox->setChecked(!checked); updateSigningWarning(); if (!checked) return; diff --git a/src/plugins/android/androidbuildapkwidget.ui b/src/plugins/android/androidbuildapkwidget.ui index e5565873af..fa3c1ef3d6 100644 --- a/src/plugins/android/androidbuildapkwidget.ui +++ b/src/plugins/android/androidbuildapkwidget.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>819</width> - <height>390</height> + <height>478</height> </rect> </property> <property name="windowTitle"> @@ -176,24 +176,75 @@ Deploying local Qt libraries is incompatible with Android 5.</string> <string>Advanced Actions</string> </property> <layout class="QGridLayout" name="gridLayout"> - <item row="2" column="0"> - <widget class="QCheckBox" name="verboseOutputCheckBox"> + <item row="0" column="0"> + <widget class="QCheckBox" name="useGradleCheckBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="text"> - <string>Verbose output</string> + <string>Use Gradle (Ant builds are deprecated)</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="deprecatedInfoIconLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Gradle builds are forced from Android SDK tools version 25.3.0 onwards as Ant scripts are no longer available.</string> + </property> + <property name="text"> + <string/> </property> </widget> </item> - <item row="1" column="0"> + <item row="0" column="2"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Preferred</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="0" colspan="3"> <widget class="QCheckBox" name="openPackageLocationCheckBox"> <property name="text"> <string>Open package location after build</string> </property> </widget> </item> - <item row="0" column="0"> - <widget class="QCheckBox" name="useGradleCheckBox"> + <item row="2" column="0" colspan="3"> + <widget class="QCheckBox" name="verboseOutputCheckBox"> + <property name="text"> + <string>Verbose output</string> + </property> + </widget> + </item> + <item row="3" column="0" colspan="3"> + <widget class="QCheckBox" name="addDebuggerCheckBox"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string>Packages debug server with the APK to enable debugging. For the signed APK this option is unchecked by default.</string> + </property> <property name="text"> - <string>Use Gradle</string> + <string>Add debug server</string> </property> </widget> </item> @@ -254,5 +305,22 @@ The APK will not be usable on any other device.</string> </customwidget> </customwidgets> <resources/> - <connections/> + <connections> + <connection> + <sender>signPackageCheckBox</sender> + <signal>clicked(bool)</signal> + <receiver>addDebuggerCheckBox</receiver> + <slot>setEnabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>113</x> + <y>178</y> + </hint> + <hint type="destinationlabel"> + <x>510</x> + <y>452</y> + </hint> + </hints> + </connection> + </connections> </ui> diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 851b0f2350..42027743b8 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -28,8 +28,11 @@ #include "androidtoolchain.h" #include "androiddevice.h" #include "androidgdbserverkitinformation.h" +#include "androidmanager.h" #include "androidqtversion.h" #include "androiddevicedialog.h" +#include "androidsdkmanager.h" +#include "androidtoolmanager.h" #include "avddialog.h" #include <coreplugin/icore.h> @@ -73,6 +76,9 @@ namespace Android { using namespace Internal; namespace { + + const QVersionNumber sdkToolsAntMissingVersion(25, 3, 0); + const QLatin1String SettingsGroup("AndroidConfigurations"); const QLatin1String SDKLocationKey("SDKLocation"); const QLatin1String NDKLocationKey("NDKLocation"); @@ -107,39 +113,14 @@ namespace { const QLatin1String keytoolName("keytool"); const QLatin1String changeTimeStamp("ChangeTimeStamp"); + const QLatin1String sdkToolsVersionKey("Pkg.Revision"); + static QString sdkSettingsFileName() { return QFileInfo(Core::ICore::settings(QSettings::SystemScope)->fileName()).absolutePath() + QLatin1String("/qtcreator/android.xml"); } - bool androidDevicesLessThan(const AndroidDeviceInfo &dev1, const AndroidDeviceInfo &dev2) - { - if (dev1.serialNumber.contains(QLatin1String("????")) != dev2.serialNumber.contains(QLatin1String("????"))) - return !dev1.serialNumber.contains(QLatin1String("????")); - if (dev1.type != dev2.type) - return dev1.type == AndroidDeviceInfo::Hardware; - if (dev1.sdk != dev2.sdk) - return dev1.sdk < dev2.sdk; - if (dev1.avdname != dev2.avdname) - return dev1.avdname < dev2.avdname; - - return dev1.serialNumber < dev2.serialNumber; - } - - static QStringList cleanAndroidABIs(const QStringList &abis) - { - QStringList res; - foreach (const QString &abi, abis) { - int index = abi.lastIndexOf(QLatin1Char('/')); - if (index == -1) - res << abi; - else - res << abi.mid(index + 1); - } - return res; - } - static bool is32BitUserSpace() { // Do the exact same check as android's emulator is doing: @@ -162,25 +143,6 @@ namespace { } return false; } - - // Some preview sdks use a non integer version - int apiLevelFromAndroidList(const QString &string) - { - bool ok; - int result = string.toInt(&ok); - if (ok) - return result; - Utils::FileName sdkLocation = AndroidConfigurations::currentConfig().sdkLocation(); - sdkLocation.appendPath(QLatin1String("/platforms/android-") + string + QLatin1String("/source.properties")); - result = QSettings(sdkLocation.toString(), QSettings::IniFormat).value(QLatin1String("AndroidVersion.ApiLevel")).toInt(&ok); - if (ok) - return result; - if (string == QLatin1String("L")) - return 21; - if (string == QLatin1String("MNC")) - return 22; - return 23; // At least - } } ////////////////////////////////// @@ -359,61 +321,14 @@ void AndroidConfig::updateNdkInformation() const m_NdkInformationUpToDate = true; } -bool AndroidConfig::sortSdkPlatformByApiLevel(const SdkPlatform &a, const SdkPlatform &b) -{ - if (a.apiLevel != b.apiLevel) - return a.apiLevel > b.apiLevel; - if (a.name != b.name) - return a.name < b.name; - return false; -} - void AndroidConfig::updateAvailableSdkPlatforms() const { if (m_availableSdkPlatformsUpToDate) return; - m_availableSdkPlatforms.clear(); - - SynchronousProcess proc; - proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment()); - SynchronousProcessResponse response - = proc.runBlocking(androidToolPath().toString(), - QStringList({"list", "target"})); // list available AVDs - if (response.result != SynchronousProcessResponse::Finished) - return; - - SdkPlatform platform; - foreach (const QString &l, response.allOutput().split('\n')) { - const QString line = l.trimmed(); - if (line.startsWith(QLatin1String("id:")) && line.contains(QLatin1String("android-"))) { - int index = line.indexOf(QLatin1String("\"android-")); - if (index == -1) - continue; - QString androidTarget = line.mid(index + 1, line.length() - index - 2); - const QString tmp = androidTarget.mid(androidTarget.lastIndexOf(QLatin1Char('-')) + 1); - platform.apiLevel = apiLevelFromAndroidList(tmp); - } else if (line.startsWith(QLatin1String("Name:"))) { - platform.name = line.mid(6); - } else if (line.startsWith(QLatin1String("Tag/ABIs :"))) { - platform.abis = cleanAndroidABIs(line.mid(10).trimmed().split(QLatin1String(", "))); - } else if (line.startsWith(QLatin1String("ABIs"))) { - platform.abis = cleanAndroidABIs(line.mid(6).trimmed().split(QLatin1String(", "))); - } else if (line.startsWith(QLatin1String("---")) || line.startsWith(QLatin1String("==="))) { - if (platform.apiLevel == -1) - continue; - auto it = std::lower_bound(m_availableSdkPlatforms.begin(), m_availableSdkPlatforms.end(), - platform, sortSdkPlatformByApiLevel); - m_availableSdkPlatforms.insert(it, platform); - platform = SdkPlatform(); - } - } - - if (platform.apiLevel != -1) { - auto it = std::lower_bound(m_availableSdkPlatforms.begin(), m_availableSdkPlatforms.end(), - platform, sortSdkPlatformByApiLevel); - m_availableSdkPlatforms.insert(it, platform); - } + m_availableSdkPlatforms.clear(); + AndroidSdkManager sdkManager(*this); + m_availableSdkPlatforms = sdkManager.availableSdkPlatforms(); m_availableSdkPlatformsUpToDate = true; } @@ -446,18 +361,6 @@ FileName AndroidConfig::adbToolPath() const return path.appendPath(QLatin1String("platform-tools/adb" QTC_HOST_EXE_SUFFIX)); } -Environment AndroidConfig::androidToolEnvironment() const -{ - Environment env = Environment::systemEnvironment(); - if (!m_openJDKLocation.isEmpty()) { - env.set(QLatin1String("JAVA_HOME"), m_openJDKLocation.toUserOutput()); - Utils::FileName binPath = m_openJDKLocation; - binPath.appendPath(QLatin1String("bin")); - env.prependOrSetPath(binPath.toUserOutput()); - } - return env; -} - FileName AndroidConfig::androidToolPath() const { if (HostOsInfo::isWindowsHost()) { @@ -486,7 +389,10 @@ FileName AndroidConfig::antToolPath() const FileName AndroidConfig::emulatorToolPath() const { FileName path = m_sdkLocation; - return path.appendPath(QLatin1String("tools/emulator" QTC_HOST_EXE_SUFFIX)); + QString relativePath = "emulator/emulator"; + if (sdkToolsVersion() < QVersionNumber(25, 3, 0)) + relativePath = "tools/emulator"; + return path.appendPath(relativePath + QTC_HOST_EXE_SUFFIX); } FileName AndroidConfig::toolPath(const Abi &abi, const QString &ndkToolChainVersion) const @@ -499,6 +405,26 @@ FileName AndroidConfig::toolPath(const Abi &abi, const QString &ndkToolChainVers .arg(toolsPrefix(abi))); } +FileName AndroidConfig::sdkManagerToolPath() const +{ + FileName sdkPath = m_sdkLocation; + QString toolPath = "tools/bin/sdkmanager"; + if (HostOsInfo::isWindowsHost()) + toolPath += ANDROID_BAT_SUFFIX; + sdkPath = sdkPath.appendPath(toolPath); + return sdkPath; +} + +FileName AndroidConfig::avdManagerToolPath() const +{ + FileName avdManagerPath = m_sdkLocation; + QString toolPath = "tools/bin/avdmanager"; + if (HostOsInfo::isWindowsHost()) + toolPath += ANDROID_BAT_SUFFIX; + avdManagerPath = avdManagerPath.appendPath(toolPath); + return avdManagerPath; +} + FileName AndroidConfig::gccPath(const Abi &abi, Core::Id lang, const QString &ndkToolChainVersion) const { @@ -583,7 +509,7 @@ QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(const QString &adbToo devices.push_back(dev); } - Utils::sort(devices, androidDevicesLessThan); + Utils::sort(devices); if (devices.isEmpty() && error) *error = QApplication::translate("AndroidConfiguration", "No devices found in output of: %1") @@ -605,197 +531,6 @@ AndroidConfig::CreateAvdInfo AndroidConfig::gatherCreateAVDInfo(QWidget *parent, return result; } -QFuture<AndroidConfig::CreateAvdInfo> AndroidConfig::createAVD(CreateAvdInfo info) const -{ - return Utils::runAsync(&AndroidConfig::createAVDImpl, info, - androidToolPath(), androidToolEnvironment()); -} - -AndroidConfig::CreateAvdInfo AndroidConfig::createAVDImpl(CreateAvdInfo info, FileName androidToolPath, Environment env) -{ - QProcess proc; - proc.setProcessEnvironment(env.toProcessEnvironment()); - QStringList arguments; - arguments << QLatin1String("create") << QLatin1String("avd") - << QLatin1String("-t") << info.target - << QLatin1String("-n") << info.name - << QLatin1String("-b") << info.abi; - if (info.sdcardSize > 0) - arguments << QLatin1String("-c") << QString::fromLatin1("%1M").arg(info.sdcardSize); - proc.start(androidToolPath.toString(), arguments); - if (!proc.waitForStarted()) { - info.error = QApplication::translate("AndroidConfig", "Could not start process \"%1 %2\"") - .arg(androidToolPath.toString(), arguments.join(QLatin1Char(' '))); - return info; - } - QTC_CHECK(proc.state() == QProcess::Running); - proc.write(QByteArray("yes\n")); // yes to "Do you wish to create a custom hardware profile" - - QByteArray question; - while (true) { - proc.waitForReadyRead(500); - question += proc.readAllStandardOutput(); - if (question.endsWith(QByteArray("]:"))) { - // truncate to last line - int index = question.lastIndexOf(QByteArray("\n")); - if (index != -1) - question = question.mid(index); - if (question.contains("hw.gpu.enabled")) - proc.write(QByteArray("yes\n")); - else - proc.write(QByteArray("\n")); - question.clear(); - } - - if (proc.state() != QProcess::Running) - break; - } - QTC_CHECK(proc.state() == QProcess::NotRunning); - - QString errorOutput = QString::fromLocal8Bit(proc.readAllStandardError()); - // The exit code is always 0, so we need to check stderr - // For now assume that any output at all indicates a error - if (!errorOutput.isEmpty()) { - info.error = errorOutput; - } - - return info; -} - -bool AndroidConfig::removeAVD(const QString &name) const -{ - SynchronousProcess proc; - proc.setTimeoutS(5); - proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment()); - SynchronousProcessResponse response - = proc.runBlocking(androidToolPath().toString(), QStringList({"delete", "avd", "-n", name})); - return response.result == SynchronousProcessResponse::Finished && response.exitCode == 0; -} - -QFuture<QVector<AndroidDeviceInfo>> AndroidConfig::androidVirtualDevicesFuture() const -{ - return Utils::runAsync(&AndroidConfig::androidVirtualDevices, - androidToolPath().toString(), androidToolEnvironment()); -} - -QVector<AndroidDeviceInfo> AndroidConfig::androidVirtualDevices(const QString &androidTool, const Environment &environment) -{ - QVector<AndroidDeviceInfo> devices; - SynchronousProcess proc; - proc.setTimeoutS(20); - proc.setProcessEnvironment(environment.toProcessEnvironment()); - SynchronousProcessResponse response = proc.run(androidTool, {"list", "avd"}); // list available AVDs - if (response.result != SynchronousProcessResponse::Finished) - return devices; - - QStringList avds = response.allOutput().split('\n'); - if (avds.empty()) - return devices; - - while (avds.first().startsWith(QLatin1String("* daemon"))) - avds.removeFirst(); // remove the daemon logs - avds.removeFirst(); // remove "List of devices attached" header line - - bool nextLineIsTargetLine = false; - - AndroidDeviceInfo dev; - for (int i = 0; i < avds.size(); i++) { - QString line = avds.at(i); - if (!line.contains(QLatin1String("Name:"))) - continue; - - int index = line.indexOf(QLatin1Char(':')) + 2; - if (index >= line.size()) - break; - dev.avdname = line.mid(index).trimmed(); - dev.sdk = -1; - dev.cpuAbi.clear(); - ++i; - for (; i < avds.size(); ++i) { - line = avds.at(i); - if (line.contains(QLatin1String("---------"))) - break; - - if (line.contains(QLatin1String("Target:")) || nextLineIsTargetLine) { - if (line.contains(QLatin1String("Google APIs"))) { - nextLineIsTargetLine = true; - continue; - } - - nextLineIsTargetLine = false; - - int lastIndex = line.lastIndexOf(QLatin1Char(' ')); - if (lastIndex == -1) // skip line - break; - QString tmp = line.mid(lastIndex).remove(QLatin1Char(')')).trimmed(); - dev.sdk = apiLevelFromAndroidList(tmp); - } - if (line.contains(QLatin1String("Tag/ABI:"))) { - int lastIndex = line.lastIndexOf(QLatin1Char('/')) + 1; - if (lastIndex >= line.size()) - break; - dev.cpuAbi = QStringList(line.mid(lastIndex)); - } else if (line.contains(QLatin1String("ABI:"))) { - int lastIndex = line.lastIndexOf(QLatin1Char(' ')) + 1; - if (lastIndex >= line.size()) - break; - dev.cpuAbi = QStringList(line.mid(lastIndex).trimmed()); - } - } - // armeabi-v7a devices can also run armeabi code - if (dev.cpuAbi == QStringList("armeabi-v7a")) - dev.cpuAbi << QLatin1String("armeabi"); - dev.state = AndroidDeviceInfo::OkState; - dev.type = AndroidDeviceInfo::Emulator; - if (dev.cpuAbi.isEmpty() || dev.sdk == -1) - continue; - devices.push_back(dev); - } - Utils::sort(devices, androidDevicesLessThan); - - return devices; -} - -QString AndroidConfig::startAVD(const QString &name) const -{ - if (!findAvd(name).isEmpty() || startAVDAsync(name)) - return waitForAvd(name); - return QString(); -} - -bool AndroidConfig::startAVDAsync(const QString &avdName) const -{ - QProcess *avdProcess = new QProcess(); - QObject::connect(avdProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished), - avdProcess, &QObject::deleteLater); - - // start the emulator - QStringList arguments; - if (AndroidConfigurations::force32bitEmulator()) - arguments << QLatin1String("-force-32bit"); - - arguments << QLatin1String("-partition-size") << QString::number(partitionSize()) - << QLatin1String("-avd") << avdName; - avdProcess->start(emulatorToolPath().toString(), arguments); - if (!avdProcess->waitForStarted(-1)) { - delete avdProcess; - return false; - } - return true; -} - -QString AndroidConfig::findAvd(const QString &avdName) const -{ - QVector<AndroidDeviceInfo> devices = connectedDevices(); - foreach (AndroidDeviceInfo device, devices) { - if (device.type != AndroidDeviceInfo::Emulator) - continue; - if (device.avdname == avdName) - return device.serialNumber; - } - return QString(); -} - bool AndroidConfig::isConnected(const QString &serialNumber) const { QVector<AndroidDeviceInfo> devices = connectedDevices(); @@ -806,39 +541,6 @@ bool AndroidConfig::isConnected(const QString &serialNumber) const return false; } -bool AndroidConfig::waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const -{ - // found a serial number, now wait until it's done booting... - for (int i = 0; i < 60; ++i) { - if (fi.isCanceled()) - return false; - if (hasFinishedBooting(serialNumber)) { - return true; - } else { - QThread::sleep(2); - if (!isConnected(serialNumber)) // device was disconnected - return false; - } - } - return false; -} - -QString AndroidConfig::waitForAvd(const QString &avdName, const QFutureInterface<bool> &fi) const -{ - // we cannot use adb -e wait-for-device, since that doesn't work if a emulator is already running - // 60 rounds of 2s sleeping, two minutes for the avd to start - QString serialNumber; - for (int i = 0; i < 60; ++i) { - if (fi.isCanceled()) - return QString(); - serialNumber = findAvd(avdName); - if (!serialNumber.isEmpty()) - return waitForBooted(serialNumber, fi) ? serialNumber : QString(); - QThread::sleep(2); - } - return QString(); -} - bool AndroidConfig::isBootToQt(const QString &device) const { return isBootToQt(adbToolPath().toString(), device); @@ -961,21 +663,6 @@ QString AndroidConfig::getProductModel(const QString &device) const return model; } -bool AndroidConfig::hasFinishedBooting(const QString &device) const -{ - QStringList arguments = AndroidDeviceInfo::adbSelector(device); - arguments << QLatin1String("shell") << QLatin1String("getprop") - << QLatin1String("init.svc.bootanim"); - - SynchronousProcess adbProc; - adbProc.setTimeoutS(10); - SynchronousProcessResponse response = adbProc.runBlocking(adbToolPath().toString(), arguments); - if (response.result != SynchronousProcessResponse::Finished) - return false; - QString value = response.allOutput().trimmed(); - return value == QLatin1String("stopped"); -} - QStringList AndroidConfig::getAbis(const QString &device) const { return getAbis(adbToolPath().toString(), device); @@ -1053,6 +740,19 @@ void AndroidConfig::setSdkLocation(const FileName &sdkLocation) m_availableSdkPlatformsUpToDate = false; } +QVersionNumber AndroidConfig::sdkToolsVersion() const +{ + QVersionNumber version; + if (m_sdkLocation.exists()) { + Utils::FileName sdkToolsPropertiesPath(m_sdkLocation); + sdkToolsPropertiesPath.appendPath("tools/source.properties"); + QSettings settings(sdkToolsPropertiesPath.toString(), QSettings::IniFormat); + auto versionStr = settings.value(sdkToolsVersionKey).toString(); + version = QVersionNumber::fromString(versionStr); + } + return version; +} + FileName AndroidConfig::ndkLocation() const { return m_ndkLocation; @@ -1126,9 +826,18 @@ void AndroidConfig::setAutomaticKitCreation(bool b) m_automaticKitCreation = b; } +bool AndroidConfig::antScriptsAvailable() const +{ + return sdkToolsVersion() < sdkToolsAntMissingVersion; +} + bool AndroidConfig::useGrandle() const { - return m_useGradle; + if (antScriptsAvailable()) { + return m_useGradle; + } + // Force gradle builds. + return true; } void AndroidConfig::setUseGradle(bool b) @@ -1353,6 +1062,20 @@ QStringList AndroidDeviceInfo::adbSelector(const QString &serialNumber) return QStringList({"-s", serialNumber}); } +bool AndroidDeviceInfo::operator<(const AndroidDeviceInfo &other) const +{ + if (serialNumber.contains("????") != other.serialNumber.contains("????")) + return !serialNumber.contains("????"); + if (type != other.type) + return type == AndroidDeviceInfo::Hardware; + if (sdk != other.sdk) + return sdk < other.sdk; + if (avdname != other.avdname) + return avdname < other.avdname; + + return serialNumber < other.serialNumber; +} + const AndroidConfig &AndroidConfigurations::currentConfig() { return m_instance->m_config; // ensure that m_instance is initialized @@ -1494,4 +1217,13 @@ void AndroidConfigurations::updateAndroidDevice() AndroidConfigurations *AndroidConfigurations::m_instance = 0; +bool SdkPlatform::operator <(const SdkPlatform &other) const +{ + if (apiLevel != other.apiLevel) + return apiLevel > other.apiLevel; + if (name != other.name) + return name < other.name; + return false; +} + } // namespace Android diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index 751e68d113..9107680810 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -36,6 +36,7 @@ #include <QHash> #include <QMap> #include <QFutureInterface> +#include <QVersionNumber> #include <utils/fileutils.h> @@ -68,19 +69,36 @@ public: static QStringList adbSelector(const QString &serialNumber); - bool isValid() { return !serialNumber.isEmpty() || !avdname.isEmpty(); } + bool isValid() const { return !serialNumber.isEmpty() || !avdname.isEmpty(); } + bool operator<(const AndroidDeviceInfo &other) const; }; +using AndroidDeviceInfoList = QList<AndroidDeviceInfo>; + +//! Defines an Android system image. +class SystemImage +{ +public: + bool isValid() const { return (apiLevel != -1) && !abiName.isEmpty(); } + int apiLevel = -1; + QString abiName; + QString package; + Utils::FileName installedLocation; +}; +using SystemImageList = QList<SystemImage>; + class SdkPlatform { public: - SdkPlatform() - : apiLevel(-1) - {} - int apiLevel; + bool isValid() const { return !name.isEmpty() && apiLevel != -1; } + bool operator <(const SdkPlatform &other) const; + int apiLevel = -1; QString name; - QStringList abis; + QString package; + Utils::FileName installedLocation; + SystemImageList systemImages; }; +using SdkPlatformList = QList<SdkPlatform>; class ANDROID_EXPORT AndroidConfig { @@ -94,6 +112,7 @@ public: Utils::FileName sdkLocation() const; void setSdkLocation(const Utils::FileName &sdkLocation); + QVersionNumber sdkToolsVersion() const; Utils::FileName ndkLocation() const; void setNdkLocation(const Utils::FileName &ndkLocation); @@ -116,18 +135,21 @@ public: bool automaticKitCreation() const; void setAutomaticKitCreation(bool b); + bool antScriptsAvailable() const; + bool useGrandle() const; void setUseGradle(bool b); Utils::FileName adbToolPath() const; Utils::FileName androidToolPath() const; - Utils::Environment androidToolEnvironment() const; Utils::FileName antToolPath() const; Utils::FileName emulatorToolPath() const; - + Utils::FileName sdkManagerToolPath() const; + Utils::FileName avdManagerToolPath() const; Utils::FileName gccPath(const ProjectExplorer::Abi &abi, Core::Id lang, const QString &ndkToolChainVersion) const; + Utils::FileName gdbPath(const ProjectExplorer::Abi &abi, const QString &ndkToolChainVersion) const; Utils::FileName keytoolPath() const; @@ -135,7 +157,8 @@ public: class CreateAvdInfo { public: - QString target; + bool isValid() const { return target.isValid() && !name.isEmpty(); } + SdkPlatform target; QString name; QString abi; int sdcardSize = 0; @@ -143,19 +166,10 @@ public: }; CreateAvdInfo gatherCreateAVDInfo(QWidget *parent, int minApiLevel = 0, QString targetArch = QString()) const; - QFuture<CreateAvdInfo> createAVD(CreateAvdInfo info) const; - bool removeAVD(const QString &name) const; QVector<AndroidDeviceInfo> connectedDevices(QString *error = 0) const; static QVector<AndroidDeviceInfo> connectedDevices(const QString &adbToolPath, QString *error = 0); - QFuture<QVector<AndroidDeviceInfo> > androidVirtualDevicesFuture() const; - static QVector<AndroidDeviceInfo> androidVirtualDevices(const QString &androidTool, const Utils::Environment &environment); - - QString startAVD(const QString &name) const; - bool startAVDAsync(const QString &avdName) const; - QString findAvd(const QString &avdName) const; - QString waitForAvd(const QString &avdName, const QFutureInterface<bool> &fi = QFutureInterface<bool>()) const; QString bestNdkPlatformMatch(int target) const; static ProjectExplorer::Abi abiForToolChainPrefix(const QString &toolchainPrefix); @@ -166,13 +180,10 @@ public: QString getProductModel(const QString &device) const; enum class OpenGl { Enabled, Disabled, Unknown }; OpenGl getOpenGLEnabled(const QString &emulator) const; - bool hasFinishedBooting(const QString &device) const; - bool waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const; bool isConnected(const QString &serialNumber) const; SdkPlatform highestAndroidSdk() const; private: - static CreateAvdInfo createAVDImpl(CreateAvdInfo info, Utils::FileName androidToolPath, Utils::Environment env); static QString getDeviceProperty(const QString &adbToolPath, const QString &device, const QString &property); Utils::FileName toolPath(const ProjectExplorer::Abi &abi, const QString &ndkToolChainVersion) const; @@ -196,12 +207,11 @@ private: QStringList m_makeExtraSearchDirectories; unsigned m_partitionSize = 1024; bool m_automaticKitCreation = true; - bool m_useGradle = false; + bool m_useGradle = true; // Ant builds are deprecated. //caches mutable bool m_availableSdkPlatformsUpToDate = false; - mutable QVector<SdkPlatform> m_availableSdkPlatforms; - static bool sortSdkPlatformByApiLevel(const SdkPlatform &a, const SdkPlatform &b); + mutable SdkPlatformList m_availableSdkPlatforms; mutable bool m_NdkInformationUpToDate = false; mutable QString m_toolchainHost; @@ -247,3 +257,5 @@ private: }; } // namespace Android +Q_DECLARE_METATYPE(Android::SdkPlatform) + diff --git a/src/plugins/android/androiddebugsupport.cpp b/src/plugins/android/androiddebugsupport.cpp index 76a3493c7e..aaffde81d4 100644 --- a/src/plugins/android/androiddebugsupport.cpp +++ b/src/plugins/android/androiddebugsupport.cpp @@ -180,6 +180,16 @@ AndroidDebugSupport::AndroidDebugSupport(RunControl *runControl) [this](const QString &output) { this->runControl()->showMessage(output, AppOutput); }); + + QTC_ASSERT(runControl, return); + auto formatter = qobject_cast<AndroidOutputFormatter*>(runControl->outputFormatter()); + QTC_ASSERT(formatter, return); + connect(m_runner, &AndroidRunner::pidFound, formatter, &AndroidOutputFormatter::appendPid); + connect(m_runner, &AndroidRunner::pidLost, formatter, &AndroidOutputFormatter::removePid); + connect(m_runner, &AndroidRunner::remoteProcessFinished, formatter, + [formatter] { + formatter->removePid(-1); + }); } void AndroidDebugSupport::handleRemoteProcessStarted(Utils::Port gdbServerPort, Utils::Port qmlPort) diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp index 98efc64ffe..b4bfbc7bc1 100644 --- a/src/plugins/android/androiddeployqtstep.cpp +++ b/src/plugins/android/androiddeployqtstep.cpp @@ -33,6 +33,7 @@ #include "androidmanager.h" #include "androidconstants.h" #include "androidglobal.h" +#include "androidavdmanager.h" #include <coreplugin/fileutils.h> #include <coreplugin/icore.h> @@ -262,8 +263,9 @@ bool AndroidDeployQtStep::init(QList<const BuildStep *> &earlierSteps) m_adbPath = AndroidConfigurations::currentConfig().adbToolPath().toString(); - if (AndroidConfigurations::currentConfig().findAvd(m_avdName).isEmpty()) - AndroidConfigurations::currentConfig().startAVDAsync(m_avdName); + AndroidAvdManager avdManager; + if (avdManager.findAvd(m_avdName).isEmpty()) + avdManager.startAvdAsync(m_avdName); return true; } @@ -414,7 +416,7 @@ void AndroidDeployQtStep::slotSetSerialNumber(const QString &serialNumber) void AndroidDeployQtStep::run(QFutureInterface<bool> &fi) { if (!m_avdName.isEmpty()) { - QString serialNumber = AndroidConfigurations::currentConfig().waitForAvd(m_avdName, fi); + QString serialNumber = AndroidAvdManager().waitForAvd(m_avdName, fi); if (serialNumber.isEmpty()) { reportRunResult(fi, false); return; diff --git a/src/plugins/android/androiddevicedialog.cpp b/src/plugins/android/androiddevicedialog.cpp index 519f125f45..451abf2dc5 100644 --- a/src/plugins/android/androiddevicedialog.cpp +++ b/src/plugins/android/androiddevicedialog.cpp @@ -25,6 +25,7 @@ #include "androiddevicedialog.h" #include "androidmanager.h" +#include "androidavdmanager.h" #include "ui_androiddevicedialog.h" #include <utils/environment.h> @@ -423,7 +424,8 @@ AndroidDeviceDialog::AndroidDeviceDialog(int apiLevel, const QString &abi, Andro m_ui(new Ui::AndroidDeviceDialog), m_apiLevel(apiLevel), m_abi(abi), - m_defaultDevice(serialNumber) + m_defaultDevice(serialNumber), + m_avdManager(new AndroidAvdManager) { m_ui->setupUi(this); m_ui->deviceView->setModel(m_model); @@ -515,7 +517,7 @@ void AndroidDeviceDialog::refreshDeviceList() m_ui->refreshDevicesButton->setEnabled(false); m_progressIndicator->show(); m_connectedDevices = AndroidConfig::connectedDevices(AndroidConfigurations::currentConfig().adbToolPath().toString()); - m_futureWatcherRefreshDevices.setFuture(AndroidConfigurations::currentConfig().androidVirtualDevicesFuture()); + m_futureWatcherRefreshDevices.setFuture(m_avdManager->avdList()); } void AndroidDeviceDialog::devicesRefreshed() @@ -530,7 +532,7 @@ void AndroidDeviceDialog::devicesRefreshed() serialNumber = deviceType == AndroidDeviceInfo::Hardware ? info.serialNumber : info.avdname; } - QVector<AndroidDeviceInfo> devices = m_futureWatcherRefreshDevices.result(); + AndroidDeviceInfoList devices = m_futureWatcherRefreshDevices.result(); QSet<QString> startedAvds = Utils::transform<QSet>(m_connectedDevices, [] (const AndroidDeviceInfo &info) { return info.avdname; @@ -583,12 +585,12 @@ void AndroidDeviceDialog::createAvd() m_ui->createAVDButton->setEnabled(false); AndroidConfig::CreateAvdInfo info = AndroidConfigurations::currentConfig().gatherCreateAVDInfo(this, m_apiLevel, m_abi); - if (info.target.isEmpty()) { + if (!info.target.isValid()) { m_ui->createAVDButton->setEnabled(true); return; } - m_futureWatcherAddDevice.setFuture(AndroidConfigurations::currentConfig().createAVD(info)); + m_futureWatcherAddDevice.setFuture(m_avdManager->createAvd(info)); } void AndroidDeviceDialog::avdAdded() diff --git a/src/plugins/android/androiddevicedialog.h b/src/plugins/android/androiddevicedialog.h index b4d7940048..8f957aa0b2 100644 --- a/src/plugins/android/androiddevicedialog.h +++ b/src/plugins/android/androiddevicedialog.h @@ -32,6 +32,8 @@ #include <QFutureWatcher> #include <QTime> +#include <memory> + QT_BEGIN_NAMESPACE class QModelIndex; QT_END_NAMESPACE @@ -41,6 +43,7 @@ namespace Utils { class ProgressIndicator; } namespace Android { namespace Internal { +class AndroidAvdManager; class AndroidDeviceModel; namespace Ui { class AndroidDeviceDialog; } @@ -74,9 +77,10 @@ private: QString m_abi; QString m_avdNameFromAdd; QString m_defaultDevice; + std::unique_ptr<AndroidAvdManager> m_avdManager; QVector<AndroidDeviceInfo> m_connectedDevices; QFutureWatcher<AndroidConfig::CreateAvdInfo> m_futureWatcherAddDevice; - QFutureWatcher<QVector<AndroidDeviceInfo>> m_futureWatcherRefreshDevices; + QFutureWatcher<AndroidDeviceInfoList> m_futureWatcherRefreshDevices; }; } diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp index 0459355b04..ee57399060 100644 --- a/src/plugins/android/androidmanager.cpp +++ b/src/plugins/android/androidmanager.cpp @@ -34,6 +34,7 @@ #include "androidqtsupport.h" #include "androidqtversion.h" #include "androidbuildapkstep.h" +#include "androidavdmanager.h" #include <coreplugin/documentmanager.h> #include <coreplugin/messagemanager.h> @@ -60,11 +61,13 @@ #include <QMessageBox> #include <QApplication> #include <QDomDocument> +#include <QVersionNumber> namespace { const QLatin1String AndroidManifestName("AndroidManifest.xml"); const QLatin1String AndroidDefaultPropertiesName("project.properties"); const QLatin1String AndroidDeviceSn("AndroidDeviceSerialNumber"); + const QLatin1String ApiLevelKey("AndroidVersion.ApiLevel"); } // anonymous namespace @@ -343,7 +346,7 @@ void AndroidManager::cleanLibsOnDevice(ProjectExplorer::Target *target) QString deviceSerialNumber = info.serialNumber; if (info.type == AndroidDeviceInfo::Emulator) { - deviceSerialNumber = AndroidConfigurations::currentConfig().startAVD(info.avdname); + deviceSerialNumber = AndroidAvdManager().startAvd(info.avdname); if (deviceSerialNumber.isEmpty()) Core::MessageManager::write(tr("Starting Android virtual device failed.")); } @@ -372,7 +375,7 @@ void AndroidManager::installQASIPackage(ProjectExplorer::Target *target, const Q QString deviceSerialNumber = info.serialNumber; if (info.type == AndroidDeviceInfo::Emulator) { - deviceSerialNumber = AndroidConfigurations::currentConfig().startAVD(info.avdname); + deviceSerialNumber = AndroidAvdManager().startAvd(info.avdname); if (deviceSerialNumber.isEmpty()) Core::MessageManager::write(tr("Starting Android virtual device failed.")); } @@ -565,18 +568,33 @@ bool AndroidManager::updateGradleProperties(ProjectExplorer::Target *target) gradleProperties["buildDir"] = ".build"; gradleProperties["androidCompileSdkVersion"] = buildTargetSDK(target).split(QLatin1Char('-')).last().toLocal8Bit(); if (gradleProperties["androidBuildToolsVersion"].isEmpty()) { - QString maxVersion; + QVersionNumber maxVersion; QDir buildToolsDir(AndroidConfigurations::currentConfig().sdkLocation().appendPath(QLatin1String("build-tools")).toString()); foreach (const QFileInfo &file, buildToolsDir.entryList(QDir::Dirs|QDir::NoDotAndDotDot)) { - QString ver(file.fileName()); + QVersionNumber ver = QVersionNumber::fromString(file.fileName()); if (maxVersion < ver) maxVersion = ver; } - if (maxVersion.isEmpty()) + if (maxVersion.isNull()) return false; - gradleProperties["androidBuildToolsVersion"] = maxVersion.toLocal8Bit(); + gradleProperties["androidBuildToolsVersion"] = maxVersion.toString().toLocal8Bit(); } return mergeGradleProperties(gradlePropertiesPath, gradleProperties); } +int AndroidManager::findApiLevel(const Utils::FileName &platformPath) +{ + int apiLevel = -1; + Utils::FileName propertiesPath = platformPath; + propertiesPath.appendPath("/source.properties"); + if (propertiesPath.exists()) { + QSettings sdkProperties(propertiesPath.toString(), QSettings::IniFormat); + bool validInt = false; + apiLevel = sdkProperties.value(ApiLevelKey).toInt(&validInt); + if (!validInt) + apiLevel = -1; + } + return apiLevel; +} + } // namespace Android diff --git a/src/plugins/android/androidmanager.h b/src/plugins/android/androidmanager.h index 2b79cb4c6a..ad50f45529 100644 --- a/src/plugins/android/androidmanager.h +++ b/src/plugins/android/androidmanager.h @@ -89,6 +89,7 @@ public: static AndroidQtSupport *androidQtSupport(ProjectExplorer::Target *target); static bool useGradle(ProjectExplorer::Target *target); static bool updateGradleProperties(ProjectExplorer::Target *target); + static int findApiLevel(const Utils::FileName &platformPath); }; } // namespace Android diff --git a/src/plugins/android/androidrunconfiguration.cpp b/src/plugins/android/androidrunconfiguration.cpp index d2d7844e5c..54f6449753 100644 --- a/src/plugins/android/androidrunconfiguration.cpp +++ b/src/plugins/android/androidrunconfiguration.cpp @@ -29,16 +29,205 @@ #include "androidmanager.h" #include <projectexplorer/kitinformation.h> +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/projectexplorersettings.h> #include <projectexplorer/target.h> #include <qtsupport/qtoutputformatter.h> #include <qtsupport/qtkitinformation.h> +#include <QPlainTextEdit> +#include <QRegularExpression> +#include <QToolButton> #include <utils/qtcassert.h> +#include <utils/utilsicons.h> using namespace ProjectExplorer; namespace Android { +static QRegularExpression logCatRegExp("([0-9\\-]*\\s+[0-9\\-:.]*)" // 1. time + "\\s*" + "([DEIVWF])" // 2. log level + "\\/" + "(.*)" // 3. TAG + "\\(\\s*" + "(\\d+)" // 4. PID + "\\)\\:\\s" + "(.*)"); // 5. Message + +AndroidOutputFormatter::AndroidOutputFormatter(Project *project) + : QtSupport::QtOutputFormatter(project) + , m_filtersButton(new QToolButton) +{ + auto filtersMenu = new QMenu(m_filtersButton.data()); + + m_filtersButton->setToolTip(tr("Filters")); + m_filtersButton->setIcon(Utils::Icons::FILTER.icon()); + m_filtersButton->setProperty("noArrow", true); + m_filtersButton->setAutoRaise(true); + m_filtersButton->setPopupMode(QToolButton::InstantPopup); + m_filtersButton->setMenu(filtersMenu); + + auto logsMenu = filtersMenu->addMenu(tr("Log Level")); + addLogAction(All, logsMenu, tr("All")); + addLogAction(Verbose, logsMenu, tr("Verbose")); + addLogAction(Info, logsMenu, tr("Info")); + addLogAction(Debug, logsMenu, tr("Debug")); + addLogAction(Warning, logsMenu, tr("Warning")); + addLogAction(Error, logsMenu, tr("Error")); + addLogAction(Fatal, logsMenu, tr("Fatal")); + updateLogMenu(); + m_appsMenu = filtersMenu->addMenu(tr("Applications")); + appendPid(-1, tr("All")); +} + +AndroidOutputFormatter::~AndroidOutputFormatter() +{} + +QList<QWidget *> AndroidOutputFormatter::toolbarWidgets() const +{ + return QList<QWidget *>{m_filtersButton.data()}; +} + +void AndroidOutputFormatter::appendMessage(const QString &text, Utils::OutputFormat format) +{ + if (text.isEmpty()) + return; + + CachedLine line; + line.content = text; + + if (format < Utils::StdOutFormat) { + line.level = SkipFiltering; + line.pid = -1; + } else { + QRegularExpressionMatch match = logCatRegExp.match(text); + if (!match.hasMatch()) + return; + line.level = None; + + switch (match.captured(2).toLatin1()[0]) { + case 'D': line.level = Debug; break; + case 'I': line.level = Info; break; + case 'V': line.level = Verbose; break; + case 'W': line.level = Warning; break; + case 'E': line.level = Error; break; + case 'F': line.level = Fatal; break; + default: return; + } + line.pid = match.captured(4).toLongLong(); + } + + m_cachedLines.append(line); + if (m_cachedLines.size() > ProjectExplorerPlugin::projectExplorerSettings().maxAppOutputLines) + m_cachedLines.pop_front(); + + filterMessage(line); +} + +void AndroidOutputFormatter::clear() +{ + m_cachedLines.clear(); +} + +void AndroidOutputFormatter::appendPid(qint64 pid, const QString &name) +{ + if (m_pids.contains(pid)) + return; + + auto action = m_appsMenu->addAction(name); + m_pids[pid] = action; + action->setCheckable(true); + action->setChecked(pid != -1); + connect(action, &QAction::triggered, this, &AndroidOutputFormatter::applyFilter); + applyFilter(); +} + +void AndroidOutputFormatter::removePid(qint64 pid) +{ + if (pid == -1) { + for (auto action : m_pids) + m_appsMenu->removeAction(action); + m_pids.clear(); + } else { + m_appsMenu->removeAction(m_pids[pid]); + m_pids.remove(pid); + } +} + +void AndroidOutputFormatter::updateLogMenu(LogLevel set, LogLevel reset) +{ + m_logLevelFlags |= set; + m_logLevelFlags &= ~reset; + for (const auto & pair : m_logLevels) + pair.second->setChecked((m_logLevelFlags & pair.first) == pair.first); + + applyFilter(); +} + +void AndroidOutputFormatter::filterMessage(const CachedLine &line) +{ + if (line.level == SkipFiltering || m_pids[-1]->isChecked()) { + QtOutputFormatter::appendMessage(line.content, Utils::NormalMessageFormat); + } else { + // Filter Log Level + if (!(m_logLevelFlags & line.level)) + return; + + // Filter PIDs + if (!m_pids[-1]->isChecked()) { + auto it = m_pids.find(line.pid); + if (it == m_pids.end() || !(*it)->isChecked()) + return; + } + + Utils::OutputFormat format = Utils::NormalMessageFormat; + switch (line.level) { + case Debug: + format = Utils::DebugFormat; + break; + case Info: + case Verbose: + format = Utils::StdOutFormat; + break; + + case Warning: + case Error: + case Fatal: + format = Utils::StdErrFormat; + break; + default: + return; + } + + Utils::OutputFormatter::appendMessage(line.content, format); + } +} + +void AndroidOutputFormatter::applyFilter() +{ + if (!plainTextEdit()) + return; + + plainTextEdit()->clear(); + if (!m_pids[-1]->isChecked()) { + bool allOn = true; + for (auto action : m_pids) { + if (!action->isChecked()) { + allOn = false; + break; + } + } + m_pids[-1]->setChecked(allOn); + } else { + for (auto action : m_pids) + action->setChecked(true); + } + + for (const auto &line : m_cachedLines) + filterMessage(line); +} + AndroidRunConfiguration::AndroidRunConfiguration(Target *parent, Core::Id id) : RunConfiguration(parent, id) { @@ -56,7 +245,7 @@ QWidget *AndroidRunConfiguration::createConfigurationWidget() Utils::OutputFormatter *AndroidRunConfiguration::createOutputFormatter() const { - return new QtSupport::QtOutputFormatter(target()->project()); + return new AndroidOutputFormatter(target()->project()); } const QString AndroidRunConfiguration::remoteChannel() const diff --git a/src/plugins/android/androidrunconfiguration.h b/src/plugins/android/androidrunconfiguration.h index cfa54312d9..9ec42e3d78 100644 --- a/src/plugins/android/androidrunconfiguration.h +++ b/src/plugins/android/androidrunconfiguration.h @@ -28,9 +28,75 @@ #include "android_global.h" #include <projectexplorer/runconfiguration.h> +#include <qtsupport/qtoutputformatter.h> + +#include <QMenu> + +QT_BEGIN_NAMESPACE +class QToolButton; +QT_END_NAMESPACE namespace Android { +class AndroidOutputFormatter : public QtSupport::QtOutputFormatter +{ + Q_OBJECT +public: + enum LogLevel { + None = 0, + Verbose = 1, + Info = 1 << 1, + Debug = 1 << 2, + Warning = 1 << 3, + Error = 1 << 4, + Fatal = 1 << 5, + All = Verbose | Info | Debug | Warning | Error | Fatal, + SkipFiltering = ~All + }; + +public: + explicit AndroidOutputFormatter(ProjectExplorer::Project *project); + ~AndroidOutputFormatter(); + + // OutputFormatter interface + QList<QWidget*> toolbarWidgets() const override; + void appendMessage(const QString &text, Utils::OutputFormat format) override; + void clear() override; + +public slots: + void appendPid(qint64 pid, const QString &name); + void removePid(qint64 pid); + +private: + struct CachedLine { + qint64 pid; + LogLevel level; + QString content; + }; + +private: + void updateLogMenu(LogLevel set = None, LogLevel reset = None); + void filterMessage(const CachedLine &line); + + void applyFilter(); + void addLogAction(LogLevel level, QMenu *logsMenu, const QString &name) { + auto action = logsMenu->addAction(name); + m_logLevels.push_back(qMakePair(level, action)); + action->setCheckable(true); + connect(action, &QAction::triggered, this, [level, this](bool checked) { + updateLogMenu(checked ? level : None , checked ? None : level); + }); + } + +private: + int m_logLevelFlags = All; + QVector<QPair<LogLevel, QAction*>> m_logLevels; + QHash<qint64, QAction*> m_pids; + QScopedPointer<QToolButton> m_filtersButton; + QMenu *m_appsMenu; + QList<CachedLine> m_cachedLines; +}; + class ANDROID_EXPORT AndroidRunConfiguration : public ProjectExplorer::RunConfiguration { Q_OBJECT diff --git a/src/plugins/android/androidruncontrol.cpp b/src/plugins/android/androidruncontrol.cpp index 2ac929a0b1..d2a4bb1ea6 100644 --- a/src/plugins/android/androidruncontrol.cpp +++ b/src/plugins/android/androidruncontrol.cpp @@ -62,6 +62,13 @@ void AndroidRunControl::start() this, &AndroidRunControl::handleRemoteOutput); connect(m_runner, &AndroidRunner::remoteProcessFinished, this, &AndroidRunControl::handleRemoteProcessFinished); + + auto formatter = static_cast<AndroidOutputFormatter *>(outputFormatter()); + connect(m_runner, &AndroidRunner::pidFound, + formatter, &AndroidOutputFormatter::appendPid); + connect(m_runner, &AndroidRunner::pidLost, + formatter, &AndroidOutputFormatter::removePid); + appendMessage(tr("Starting remote process."), Utils::NormalMessageFormat); m_runner->setRunnable(runnable().as<AndroidRunnable>()); m_runner->start(); diff --git a/src/plugins/android/androidrunner.cpp b/src/plugins/android/androidrunner.cpp index 778d348b68..9208e69953 100644 --- a/src/plugins/android/androidrunner.cpp +++ b/src/plugins/android/androidrunner.cpp @@ -31,6 +31,7 @@ #include "androidglobal.h" #include "androidrunconfiguration.h" #include "androidmanager.h" +#include "androidavdmanager.h" #include <debugger/debuggerrunconfigurationaspect.h> #include <projectexplorer/projectexplorer.h> @@ -51,6 +52,7 @@ #include <QTime> #include <QTcpServer> #include <QTcpSocket> +#include <QRegularExpression> using namespace std; using namespace std::placeholders; @@ -125,10 +127,10 @@ namespace Internal { const int MIN_SOCKET_HANDSHAKE_PORT = 20001; const int MAX_SOCKET_HANDSHAKE_PORT = 20999; -static const QString pidScript = QStringLiteral("for p in /proc/[0-9]*; " - "do cat <$p/cmdline && echo :${p##*/}; done"); -static const QString pidPollingScript = QStringLiteral("while true; do sleep 1; " - "cat /proc/%1/cmdline > /dev/null; done"); +static const QString pidScript = QStringLiteral("input keyevent KEYCODE_WAKEUP; " + "while true; do sleep 1; echo \"=\"; " + "for p in /proc/[0-9]*; " + "do cat <$p/cmdline && echo :${p##*/}; done; done"); static const QString regExpLogcat = QStringLiteral("[0-9\\-]*" // date "\\s+" @@ -146,55 +148,26 @@ static const QString regExpLogcat = QStringLiteral("[0-9\\-]*" // date ); static int APP_START_TIMEOUT = 45000; -static bool isTimedOut(const chrono::high_resolution_clock::time_point &start, - int msecs = APP_START_TIMEOUT) -{ - bool timedOut = false; - auto end = chrono::high_resolution_clock::now(); - if (chrono::duration_cast<chrono::milliseconds>(end-start).count() > msecs) - timedOut = true; - return timedOut; -} - -static qint64 extractPID(const QByteArray &output, const QString &packageName) -{ - qint64 pid = -1; - foreach (auto tuple, output.split('\n')) { - tuple = tuple.simplified(); - if (!tuple.isEmpty()) { - auto parts = tuple.split(':'); - QString commandName = QString::fromLocal8Bit(parts.first()); - if (parts.length() == 2 && commandName == packageName) { - pid = parts.last().toLongLong(); - break; - } - } - } - return pid; -} +enum class PidStatus { + Found, + Lost +}; -void findProcessPID(QFutureInterface<qint64> &fi, const QString &adbPath, - QStringList selector, const QString &packageName) +struct PidInfo { - if (packageName.isEmpty()) - return; - - qint64 processPID = -1; - chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now(); - do { - QThread::msleep(200); - const QByteArray out = Utils::SynchronousProcess() - .runBlocking(adbPath, selector << QStringLiteral("shell") << pidScript) - .allRawOutput(); - processPID = extractPID(out, packageName); - } while (processPID == -1 && !isTimedOut(start) && !fi.isCanceled()); - - if (!fi.isCanceled()) - fi.reportResult(processPID); -} + PidInfo(qint64 pid = -1, PidStatus status = PidStatus::Lost, QString name = {}) + : pid(pid) + , status(status) + , name(name) + {} + qint64 pid; + PidStatus status; + QString name; +}; static void deleter(QProcess *p) { + p->disconnect(); p->kill(); p->waitForFinished(); // Might get deleted from its own signal handler. @@ -228,29 +201,31 @@ signals: void remoteOutput(const QString &output); void remoteErrorOutput(const QString &output); + void pidFound(qint64, const QString &name); + void pidLost(qint64); private: - void onProcessIdChanged(qint64 pid); + void findProcessPids(); + void onProcessIdChanged(PidInfo pidInfo); void logcatReadStandardError(); void logcatReadStandardOutput(); void adbKill(qint64 pid); QStringList selector() const { return m_selector; } void forceStop(); - void findPs(); void logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError); bool adbShellAmNeedsQuotes(); bool runAdb(const QStringList &args, QString *exitMessage = nullptr, int timeoutS = 10); + int deviceSdkVersion(); // Create the processes and timer in the worker thread, for correct thread affinity std::unique_ptr<QProcess, decltype(&deleter)> m_adbLogcatProcess; - std::unique_ptr<QProcess, decltype(&deleter)> m_psIsAlive; + std::unique_ptr<QProcess, decltype(&deleter)> m_pidsFinderProcess; QScopedPointer<QTcpSocket> m_socket; QByteArray m_stdoutBuffer; QByteArray m_stderrBuffer; - QFuture<qint64> m_pidFinder; - qint64 m_processPID = -1; + QSet<qint64> m_processPids; bool m_useCppDebugger = false; QmlDebug::QmlDebugServicesPreset m_qmlDebugServices; Utils::Port m_localGdbServerPort; // Local end of forwarded debug socket. @@ -261,20 +236,20 @@ private: QString m_gdbserverSocket; QString m_adb; QStringList m_selector; - QRegExp m_logCatRegExp; DebugHandShakeType m_handShakeMethod = SocketHandShake; bool m_customPort = false; QString m_packageName; int m_socketHandShakePort = MIN_SOCKET_HANDSHAKE_PORT; + QByteArray m_pidsBuffer; + QScopedPointer<QTimer> m_timeoutTimer; }; AndroidRunnerWorker::AndroidRunnerWorker(AndroidRunConfiguration *runConfig, Core::Id runMode, const QString &packageName, const QStringList &selector) : m_adbLogcatProcess(nullptr, deleter) - , m_psIsAlive(nullptr, deleter) + , m_pidsFinderProcess(nullptr, deleter) , m_selector(selector) - , m_logCatRegExp(regExpLogcat) , m_packageName(packageName) { Debugger::DebuggerRunConfigurationAspect *aspect @@ -338,23 +313,18 @@ AndroidRunnerWorker::AndroidRunnerWorker(AndroidRunConfiguration *runConfig, Cor AndroidRunnerWorker::~AndroidRunnerWorker() { - if (!m_pidFinder.isFinished()) - m_pidFinder.cancel(); } void AndroidRunnerWorker::forceStop() { runAdb(selector() << "shell" << "am" << "force-stop" << m_packageName, nullptr, 30); - // try killing it via kill -9 - const QByteArray out = Utils::SynchronousProcess() - .runBlocking(m_adb, selector() << QStringLiteral("shell") << pidScript) - .allRawOutput(); - - qint64 pid = extractPID(out.simplified(), m_packageName); - if (pid != -1) { - adbKill(pid); + for (auto it = m_processPids.constBegin(); it != m_processPids.constEnd(); ++it) { + emit pidLost(*it); + adbKill(*it); } + m_processPids.clear(); + m_pidsBuffer.clear(); } void AndroidRunnerWorker::asyncStart(const QString &intentName, @@ -368,8 +338,12 @@ void AndroidRunnerWorker::asyncStart(const QString &intentName, this, &AndroidRunnerWorker::logcatReadStandardOutput); connect(logcatProcess.get(), &QProcess::readyReadStandardError, this, &AndroidRunnerWorker::logcatReadStandardError); + // Its assumed that the device or avd returned by selector() is online. - logcatProcess->start(m_adb, selector() << "logcat"); + QStringList logcatArgs = selector() << "logcat" << "-v" << "time"; + if (deviceSdkVersion() > 20) + logcatArgs << "-T" << "0"; + logcatProcess->start(m_adb, logcatArgs); QString errorMessage; @@ -507,9 +481,20 @@ void AndroidRunnerWorker::asyncStart(const QString &intentName, QTC_ASSERT(!m_adbLogcatProcess, /**/); m_adbLogcatProcess = std::move(logcatProcess); - m_pidFinder = Utils::onResultReady(Utils::runAsync(&findProcessPID, m_adb, selector(), - m_packageName), - bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1)); + + m_timeoutTimer.reset(new QTimer); + m_timeoutTimer->setSingleShot(true); + connect(m_timeoutTimer.data(), &QTimer::timeout, + this,[this] { onProcessIdChanged(PidInfo{}); }); + m_timeoutTimer->start(APP_START_TIMEOUT); + + m_pidsFinderProcess.reset(new QProcess); + m_pidsFinderProcess->setProcessChannelMode(QProcess::MergedChannels); + connect(m_pidsFinderProcess.get(), &QProcess::readyRead, this, &AndroidRunnerWorker::findProcessPids); + connect(m_pidsFinderProcess.get(), + static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), + this, [this] { onProcessIdChanged(PidInfo{}); }); + m_pidsFinderProcess->start(m_adb, selector() << "shell" << pidScript); } bool AndroidRunnerWorker::adbShellAmNeedsQuotes() @@ -545,6 +530,19 @@ bool AndroidRunnerWorker::runAdb(const QStringList &args, QString *exitMessage, return response.result == Utils::SynchronousProcessResponse::Finished; } +int AndroidRunnerWorker::deviceSdkVersion() +{ + Utils::SynchronousProcess adb; + adb.setTimeoutS(10); + Utils::SynchronousProcessResponse response + = adb.run(m_adb, selector() << "shell" << "getprop" << "ro.build.version.sdk"); + if (response.result == Utils::SynchronousProcessResponse::StartFailed + || response.result != Utils::SynchronousProcessResponse::Finished) + return -1; + + return response.allOutput().trimmed().toInt(); +} + void AndroidRunnerWorker::handleRemoteDebuggerRunning() { if (m_useCppDebugger) { @@ -558,21 +556,79 @@ void AndroidRunnerWorker::handleRemoteDebuggerRunning() runAdb(selector() << "push" << tmp.fileName() << m_pongFile); } - QTC_CHECK(m_processPID != -1); + QTC_CHECK(!m_processPids.isEmpty()); } emit remoteProcessStarted(m_localGdbServerPort, m_qmlPort); } -void AndroidRunnerWorker::asyncStop(const QVector<QStringList> &adbCommands) +void AndroidRunnerWorker::findProcessPids() { - if (!m_pidFinder.isFinished()) - m_pidFinder.cancel(); + static QMap<qint64, QByteArray> extractedPids; + static auto oldPids = m_processPids; - if (m_processPID != -1) { - forceStop(); + m_pidsBuffer += m_pidsFinderProcess->readAll(); + while (!m_pidsBuffer.isEmpty()) { + const int to = m_pidsBuffer.indexOf('\n'); + if (to < 0) + break; + + if (to == 0) { + m_pidsBuffer = m_pidsBuffer.mid(1); + continue; + } + + // = is used to delimit ps outputs + // is needed to know when an existins PID is killed + if (m_pidsBuffer[0] != '=') { + QByteArray tuple = m_pidsBuffer.left(to + 1).simplified(); + QList<QByteArray> parts = tuple.split(':'); + QByteArray commandName = parts.takeFirst(); + if (QString::fromLocal8Bit(commandName) == m_packageName) { + auto pid = parts.last().toLongLong(); + if (!m_processPids.contains(pid)) { + extractedPids[pid] = commandName + (parts.length() == 2 + ? ":" + parts.first() : QByteArray{}); + } else { + oldPids.remove(pid); + } + } + } else { + // Add new PIDs + for (auto it = extractedPids.constBegin(); it != extractedPids.constEnd(); ++it) { + onProcessIdChanged(PidInfo(it.key(), PidStatus::Found, + QString::fromLocal8Bit(it.value()))); + } + extractedPids.clear(); + + // Remove the dead ones + for (auto it = oldPids.constBegin(); it != oldPids.constEnd(); ++it) + onProcessIdChanged(PidInfo(*it, PidStatus::Lost)); + + // Save the current non dead PIDs + oldPids = m_processPids; + if (m_processPids.isEmpty()) { + extractedPids.clear(); + m_pidsBuffer.clear(); + break; + } + } + m_pidsBuffer = m_pidsBuffer.mid(to + 1); } +} + +void AndroidRunnerWorker::asyncStop(const QVector<QStringList> &adbCommands) +{ + m_timeoutTimer.reset(); + m_pidsFinderProcess.reset(); + if (!m_processPids.isEmpty()) + forceStop(); + foreach (const QStringList &entry, adbCommands) runAdb(selector() << entry); + + m_adbLogcatProcess.reset(); + emit remoteProcessFinished(QLatin1String("\n\n") + + tr("\"%1\" terminated.").arg(m_packageName)); } void AndroidRunnerWorker::setAdbParameters(const QString &packageName, const QStringList &selector) @@ -594,58 +650,48 @@ void AndroidRunnerWorker::logcatProcess(const QByteArray &text, QByteArray &buff buffer.clear(); } - QString pidString = QString::number(m_processPID); foreach (const QByteArray &msg, lines) { - const QString line = QString::fromUtf8(msg).trimmed() + QLatin1Char('\n'); - if (!line.contains(pidString)) - continue; - if (m_logCatRegExp.exactMatch(line)) { - // Android M - if (m_logCatRegExp.cap(1) == pidString) { - const QString &messagetype = m_logCatRegExp.cap(2); - QString output = line.mid(m_logCatRegExp.pos(2)); - - if (onlyError - || messagetype == QLatin1String("F") - || messagetype == QLatin1String("E") - || messagetype == QLatin1String("W")) - emit remoteErrorOutput(output); - else - emit remoteOutput(output); - } - } else { - if (onlyError || line.startsWith("F/") - || line.startsWith("E/") - || line.startsWith("W/")) - emit remoteErrorOutput(line); - else - emit remoteOutput(line); - } + const QString line = QString::fromUtf8(msg.trimmed()); + if (onlyError) + emit remoteErrorOutput(line); + else + emit remoteOutput(line); } } -void AndroidRunnerWorker::onProcessIdChanged(qint64 pid) +void AndroidRunnerWorker::onProcessIdChanged(PidInfo pidInfo) { // Don't write to m_psProc from a different thread QTC_ASSERT(QThread::currentThread() == thread(), return); - m_processPID = pid; - if (m_processPID == -1) { + + auto isFirst = m_processPids.isEmpty(); + if (pidInfo.status == PidStatus::Lost) { + m_processPids.remove(pidInfo.pid); + emit pidLost(pidInfo.pid); + } else { + m_processPids.insert(pidInfo.pid); + emit pidFound(pidInfo.pid, pidInfo.name); + } + + if (m_processPids.isEmpty() || pidInfo.pid == -1) { emit remoteProcessFinished(QLatin1String("\n\n") + tr("\"%1\" died.") .arg(m_packageName)); // App died/killed. Reset log and monitor processes. + forceStop(); m_adbLogcatProcess.reset(); - m_psIsAlive.reset(); - } else { + m_timeoutTimer.reset(); + } else if (isFirst) { + m_timeoutTimer.reset(); if (m_useCppDebugger) { // This will be funneled to the engine to actually start and attach // gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below. QByteArray serverChannel = ':' + QByteArray::number(m_localGdbServerPort.number()); - emit remoteServerRunning(serverChannel, m_processPID); + emit remoteServerRunning(serverChannel, pidInfo.pid); } else if (m_qmlDebugServices == QmlDebug::QmlDebuggerServices) { // This will be funneled to the engine to actually start and attach // gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below. QByteArray serverChannel = QByteArray::number(m_qmlPort.number()); - emit remoteServerRunning(serverChannel, m_processPID); + emit remoteServerRunning(serverChannel, pidInfo.pid); } else if (m_qmlDebugServices == QmlDebug::QmlProfilerServices) { emit remoteProcessStarted(Utils::Port(), m_qmlPort); } else { @@ -653,27 +699,18 @@ void AndroidRunnerWorker::onProcessIdChanged(qint64 pid) emit remoteProcessStarted(Utils::Port(), Utils::Port()); } logcatReadStandardOutput(); - QTC_ASSERT(!m_psIsAlive, /**/); - m_psIsAlive.reset(new QProcess); - m_psIsAlive->setProcessChannelMode(QProcess::MergedChannels); - connect(m_psIsAlive.get(), &QProcess::readyRead, [this](){ - if (!m_psIsAlive->readAll().simplified().isEmpty()) - onProcessIdChanged(-1); - }); - m_psIsAlive->start(m_adb, selector() << QStringLiteral("shell") - << pidPollingScript.arg(m_processPID)); } } void AndroidRunnerWorker::logcatReadStandardError() { - if (m_processPID != -1) + if (!m_processPids.isEmpty() && m_adbLogcatProcess) logcatProcess(m_adbLogcatProcess->readAllStandardError(), m_stderrBuffer, true); } void AndroidRunnerWorker::logcatReadStandardOutput() { - if (m_processPID != -1) + if (!m_processPids.isEmpty() && m_adbLogcatProcess) logcatProcess(m_adbLogcatProcess->readAllStandardOutput(), m_stdoutBuffer, false); } @@ -724,6 +761,10 @@ AndroidRunner::AndroidRunner(QObject *parent, RunConfiguration *runConfig, Core: this, &AndroidRunner::remoteOutput); connect(m_worker.data(), &AndroidRunnerWorker::remoteErrorOutput, this, &AndroidRunner::remoteErrorOutput); + connect(m_worker.data(), &AndroidRunnerWorker::pidFound, + this, &AndroidRunner::pidFound); + connect(m_worker.data(), &AndroidRunnerWorker::pidLost, + this, &AndroidRunner::pidLost); m_thread.start(); } @@ -791,8 +832,9 @@ void AndroidRunner::launchAVD() emit adbParametersChanged(m_androidRunnable.packageName, AndroidDeviceInfo::adbSelector(info.serialNumber)); if (info.isValid()) { - if (AndroidConfigurations::currentConfig().findAvd(info.avdname).isEmpty()) { - bool launched = AndroidConfigurations::currentConfig().startAVDAsync(info.avdname); + AndroidAvdManager avdManager; + if (avdManager.findAvd(info.avdname).isEmpty()) { + bool launched = avdManager.startAvdAsync(info.avdname); m_launchedAVDName = launched ? info.avdname:""; } else { m_launchedAVDName.clear(); @@ -803,11 +845,12 @@ void AndroidRunner::launchAVD() void AndroidRunner::checkAVD() { const AndroidConfig &config = AndroidConfigurations::currentConfig(); - QString serialNumber = config.findAvd(m_launchedAVDName); + AndroidAvdManager avdManager(config); + QString serialNumber = avdManager.findAvd(m_launchedAVDName); if (!serialNumber.isEmpty()) return; // try again on next timer hit - if (config.hasFinishedBooting(serialNumber)) { + if (avdManager.isAvdBooted(serialNumber)) { m_checkAVDTimer.stop(); AndroidManager::setDeviceSerialNumber(m_runConfig->target(), serialNumber); emit asyncStart(m_androidRunnable.intentName, m_androidRunnable.beforeStartADBCommands); diff --git a/src/plugins/android/androidrunner.h b/src/plugins/android/androidrunner.h index dd35534bd6..f30dc4754b 100644 --- a/src/plugins/android/androidrunner.h +++ b/src/plugins/android/androidrunner.h @@ -75,6 +75,9 @@ signals: void adbParametersChanged(const QString &packageName, const QStringList &selector); void avdDetected(); + void pidFound(qint64, const QString &name); + void pidLost(qint64); + private: void checkAVD(); void launchAVD(); diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp new file mode 100644 index 0000000000..0ed23660a6 --- /dev/null +++ b/src/plugins/android/androidsdkmanager.cpp @@ -0,0 +1,337 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#include "androidsdkmanager.h" + +#include "androidmanager.h" +#include "androidtoolmanager.h" + +#include "utils/algorithm.h" +#include "utils/qtcassert.h" +#include "utils/synchronousprocess.h" +#include "utils/environment.h" + +#include <QLoggingCategory> +#include <QSettings> + +namespace { +Q_LOGGING_CATEGORY(sdkManagerLog, "qtc.android.sdkManager") +} + +namespace Android { +namespace Internal { + +// Though sdk manager is introduced in 25.2.3 but the verbose mode is avaialble in 25.3.0 +// and android tool is supported in 25.2.3 +const QVersionNumber sdkManagerIntroVersion(25, 3 ,0); + +const char installLocationKey[] = "Installed Location:"; +const char apiLevelPropertyKey[] = "AndroidVersion.ApiLevel"; +const char abiPropertyKey[] = "SystemImage.Abi"; + +using namespace Utils; + +/*! + Parses the \a line for a [spaces]key[spaces]value[spaces] pattern and returns + \c true if \a key is found, false otherwise. Result is copied into \a value. + */ +static bool valueForKey(QString key, const QString &line, QString *value = nullptr) +{ + auto trimmedInput = line.trimmed(); + if (trimmedInput.startsWith(key)) { + if (value) + *value = trimmedInput.section(key, 1, 1).trimmed(); + return true; + } + return false; +} + +/*! + Runs the \c sdkmanger tool specific to configuration \a config with arguments \a args. Returns + \c true if the command is successfully executed. Output is copied into \a output. The function + blocks the calling thread. + */ +static bool sdkManagerCommand(const AndroidConfig config, const QStringList &args, QString *output) +{ + QString sdkManagerToolPath = config.sdkManagerToolPath().toString(); + SynchronousProcess proc; + SynchronousProcessResponse response = proc.runBlocking(sdkManagerToolPath, args); + if (response.result == SynchronousProcessResponse::Finished) { + if (output) + *output = response.allOutput(); + return true; + } + return false; +} + +/*! + \class SdkManagerOutputParser + \brief The SdkManagerOutputParser class is a helper class to parse the output of the \c sdkmanager + commands. + */ +class SdkManagerOutputParser +{ +public: + enum MarkerTag + { + None = 0x01, + InstalledPackagesMarker = 0x02, + AvailablePackagesMarkers = 0x04, + AvailableUpdatesMarker = 0x08, + EmptyMarker = 0x10, + PlatformMarker = 0x20, + SystemImageMarker = 0x40, + SectionMarkers = InstalledPackagesMarker | AvailablePackagesMarkers | AvailableUpdatesMarker + }; + + void parsePackageListing(const QString &output); + + SdkPlatformList m_installedPlatforms; + +private: + void compileData(); + void parsePackageData(MarkerTag packageMarker, const QStringList &data); + bool parsePlatform(const QStringList &data, SdkPlatform *platform) const; + bool parseSystemImage(const QStringList &data, SystemImage *image); + MarkerTag parseMarkers(const QString &line); + + MarkerTag m_currentSection = MarkerTag::None; + SystemImageList m_installedSystemImages; +}; + +const std::map<SdkManagerOutputParser::MarkerTag, const char *> markerTags { + {SdkManagerOutputParser::MarkerTag::InstalledPackagesMarker, "Installed packages:"}, + {SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Packages:"}, + {SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Updates:"}, + {SdkManagerOutputParser::MarkerTag::PlatformMarker, "platforms"}, + {SdkManagerOutputParser::MarkerTag::SystemImageMarker, "system-images"} +}; + +AndroidSdkManager::AndroidSdkManager(const AndroidConfig &config): + m_config(config), + m_parser(new SdkManagerOutputParser) +{ + QString packageListing; + if (sdkManagerCommand(config, QStringList({"--list", "--verbose"}), &packageListing)) { + m_parser->parsePackageListing(packageListing); + } +} + +AndroidSdkManager::~AndroidSdkManager() +{ + +} + +SdkPlatformList AndroidSdkManager::availableSdkPlatforms() +{ + if (m_config.sdkToolsVersion() < sdkManagerIntroVersion) { + AndroidToolManager toolManager(m_config); + return toolManager.availableSdkPlatforms(); + } + + return m_parser->m_installedPlatforms; +} + +void SdkManagerOutputParser::parsePackageListing(const QString &output) +{ + QStringList packageData; + bool collectingPackageData = false; + MarkerTag currentPackageMarker = MarkerTag::None; + + auto processCurrentPackage = [&]() { + if (collectingPackageData) { + collectingPackageData = false; + parsePackageData(currentPackageMarker, packageData); + packageData.clear(); + } + }; + + foreach (QString outputLine, output.split('\n')) { + MarkerTag marker = parseMarkers(outputLine); + + if (marker & SectionMarkers) { + // Section marker found. Update the current section being parsed. + m_currentSection = marker; + processCurrentPackage(); + continue; + } + + if (m_currentSection == None + || m_currentSection == AvailablePackagesMarkers // At this point. Not interested in + || m_currentSection == AvailableUpdatesMarker) { // available or update packages. + // Let go of verbose output utill a valid section starts. + continue; + } + + if (marker == EmptyMarker) { + // Empty marker. Occurs at the end of a package details. + // Process the collected package data, if any. + processCurrentPackage(); + continue; + } + + if (marker == None) { + if (collectingPackageData) + packageData << outputLine; // Collect data until next marker. + else + continue; + } else { + // Package marker found. + processCurrentPackage(); // New package starts. Process the collected package data, if any. + currentPackageMarker = marker; + collectingPackageData = true; + packageData << outputLine; + } + } + compileData(); + Utils::sort(m_installedPlatforms); +} + +void SdkManagerOutputParser::compileData() +{ + // Associate the system images with sdk platforms. + for (auto &image : m_installedSystemImages) { + auto findPlatfom = [image](const SdkPlatform &platform) { + return platform.apiLevel == image.apiLevel; + }; + auto itr = std::find_if(m_installedPlatforms.begin(), m_installedPlatforms.end(), findPlatfom); + if (itr != m_installedPlatforms.end()) + itr->systemImages.append(image); + } +} + +void SdkManagerOutputParser::parsePackageData(MarkerTag packageMarker, const QStringList &data) +{ + QTC_ASSERT(!data.isEmpty() && packageMarker != None, return); + + if (m_currentSection != MarkerTag::InstalledPackagesMarker) + return; // For now, only interested in installed packages. + + switch (packageMarker) { + case MarkerTag::PlatformMarker: + { + SdkPlatform platform; + if (parsePlatform(data, &platform)) + m_installedPlatforms.append(platform); + else + qCDebug(sdkManagerLog) << "Platform: Parsing failed: " << data; + } + break; + + case MarkerTag::SystemImageMarker: + { + SystemImage image; + if (parseSystemImage(data, &image)) + m_installedSystemImages.append(image); + else + qCDebug(sdkManagerLog) << "System Image: Parsing failed: " << data; + } + break; + + default: + qCDebug(sdkManagerLog) << "Unhandled package: " << markerTags.at(packageMarker); + break; + } +} + +bool SdkManagerOutputParser::parsePlatform(const QStringList &data, SdkPlatform *platform) const +{ + QTC_ASSERT(platform && !data.isEmpty(), return false); + + QStringList parts = data.at(0).split(';'); + if (parts.count() < 2) { + qCDebug(sdkManagerLog) << "Platform: Unexpected header: "<< data; + return false; + } + platform->name = parts[1]; + platform->package = data.at(0); + + foreach (QString line, data) { + QString value; + if (valueForKey(installLocationKey, line, &value)) + platform->installedLocation = Utils::FileName::fromString(value); + } + + int apiLevel = AndroidManager::findApiLevel(platform->installedLocation); + if (apiLevel != -1) + platform->apiLevel = apiLevel; + else + qCDebug(sdkManagerLog) << "Platform: Can not parse api level: "<< data; + + return apiLevel != -1; +} + +bool SdkManagerOutputParser::parseSystemImage(const QStringList &data, SystemImage *image) +{ + QTC_ASSERT(image && !data.isEmpty(), return false); + + QStringList parts = data.at(0).split(';'); + QTC_ASSERT(!data.isEmpty() && parts.count() >= 4, + qCDebug(sdkManagerLog) << "System Image: Unexpected header: " << data); + + image->package = data.at(0); + foreach (QString line, data) { + QString value; + if (valueForKey(installLocationKey, line, &value)) + image->installedLocation = Utils::FileName::fromString(value); + } + + Utils::FileName propertiesPath = image->installedLocation; + propertiesPath.appendPath("/source.properties"); + if (propertiesPath.exists()) { + // Installed System Image. + QSettings imageProperties(propertiesPath.toString(), QSettings::IniFormat); + bool validApiLevel = false; + image->apiLevel = imageProperties.value(apiLevelPropertyKey).toInt(&validApiLevel); + if (!validApiLevel) { + qCDebug(sdkManagerLog) << "System Image: Can not parse api level: "<< data; + return false; + } + image->abiName = imageProperties.value(abiPropertyKey).toString(); + } else if (parts.count() >= 4){ + image->apiLevel = parts[1].section('-', 1, 1).toInt(); + image->abiName = parts[3]; + } else { + qCDebug(sdkManagerLog) << "System Image: Can not parse: "<< data; + return false; + } + + return true; +} + +SdkManagerOutputParser::MarkerTag SdkManagerOutputParser::parseMarkers(const QString &line) +{ + if (line.isEmpty()) + return EmptyMarker; + + for (auto pair: markerTags) { + if (line.startsWith(QLatin1String(pair.second))) + return pair.first; + } + + return None; +} + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidsdkmanager.h b/src/plugins/android/androidsdkmanager.h new file mode 100644 index 0000000000..2c11148072 --- /dev/null +++ b/src/plugins/android/androidsdkmanager.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#pragma once + +#include "utils/fileutils.h" +#include "androidconfigurations.h" + +#include <memory> + +namespace Android { +namespace Internal { + +class SdkManagerOutputParser; + +class AndroidSdkManager +{ +public: + AndroidSdkManager(const AndroidConfig &config); + ~AndroidSdkManager(); + + SdkPlatformList availableSdkPlatforms(); + +private: + const AndroidConfig &m_config; + std::unique_ptr<SdkManagerOutputParser> m_parser; +}; + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index 5cdf42cdf3..a0a24ea087 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -30,6 +30,7 @@ #include "androidconfigurations.h" #include "androidconstants.h" #include "androidtoolchain.h" +#include "androidavdmanager.h" #include <utils/environment.h> #include <utils/hostosinfo.h> @@ -58,7 +59,7 @@ namespace Android { namespace Internal { -void AvdModel::setAvdList(const QVector<AndroidDeviceInfo> &list) +void AvdModel::setAvdList(const AndroidDeviceInfoList &list) { beginResetModel(); m_list = list; @@ -128,10 +129,13 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent) m_ndkState(NotSet), m_javaState(NotSet), m_ui(new Ui_AndroidSettingsWidget), - m_androidConfig(AndroidConfigurations::currentConfig()) + m_androidConfig(AndroidConfigurations::currentConfig()), + m_avdManager(new AndroidAvdManager(m_androidConfig)) { m_ui->setupUi(this); + m_ui->deprecatedInfoIconLabel->setPixmap(Utils::Icons::INFO.pixmap()); + connect(&m_checkGdbWatcher, &QFutureWatcherBase::finished, this, &AndroidSettingsWidget::checkGdbFinished); @@ -158,7 +162,8 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent) m_ui->AntLocationPathChooser->setPromptDialogTitle(tr("Select ant Script")); m_ui->AntLocationPathChooser->setInitialBrowsePathBackup(dir); m_ui->AntLocationPathChooser->setPromptDialogFilter(filter); - m_ui->UseGradleCheckBox->setChecked(m_androidConfig.useGrandle()); + + updateGradleBuildUi(); m_ui->OpenJDKLocationPathChooser->setFileName(m_androidConfig.openJDKLocation()); m_ui->OpenJDKLocationPathChooser->setPromptDialogTitle(tr("Select JDK Path")); @@ -463,7 +468,7 @@ void AndroidSettingsWidget::enableAvdControls() void AndroidSettingsWidget::startUpdateAvd() { disableAvdControls(); - m_virtualDevicesWatcher.setFuture(m_androidConfig.androidVirtualDevicesFuture()); + m_virtualDevicesWatcher.setFuture(m_avdManager->avdList()); } void AndroidSettingsWidget::updateAvds() @@ -476,6 +481,13 @@ void AndroidSettingsWidget::updateAvds() enableAvdControls(); } +void AndroidSettingsWidget::updateGradleBuildUi() +{ + m_ui->UseGradleCheckBox->setEnabled(m_androidConfig.antScriptsAvailable()); + m_ui->UseGradleCheckBox->setChecked(!m_androidConfig.antScriptsAvailable() || + m_androidConfig.useGrandle()); +} + bool AndroidSettingsWidget::sdkLocationIsValid() const { Utils::FileName androidExe = m_androidConfig.sdkLocation(); @@ -505,6 +517,7 @@ void AndroidSettingsWidget::saveSettings() void AndroidSettingsWidget::sdkLocationEditingFinished() { m_androidConfig.setSdkLocation(Utils::FileName::fromUserInput(m_ui->SDKLocationPathChooser->rawPath())); + updateGradleBuildUi(); check(Sdk); @@ -587,12 +600,12 @@ void AndroidSettingsWidget::addAVD() disableAvdControls(); AndroidConfig::CreateAvdInfo info = m_androidConfig.gatherCreateAVDInfo(this); - if (info.target.isEmpty()) { + if (!info.target.isValid()) { enableAvdControls(); return; } - m_futureWatcher.setFuture(m_androidConfig.createAVD(info)); + m_futureWatcher.setFuture(m_avdManager->createAvd(info)); } void AndroidSettingsWidget::avdAdded() @@ -620,13 +633,13 @@ void AndroidSettingsWidget::removeAVD() return; } - m_androidConfig.removeAVD(avdName); + m_avdManager->removeAvd(avdName); startUpdateAvd(); } void AndroidSettingsWidget::startAVD() { - m_androidConfig.startAVDAsync(m_AVDModel.avdName(m_ui->AVDTableView->currentIndex())); + m_avdManager->startAvdAsync(m_AVDModel.avdName(m_ui->AVDTableView->currentIndex())); } void AndroidSettingsWidget::avdActivated(const QModelIndex &index) @@ -671,16 +684,15 @@ void AndroidSettingsWidget::showGdbWarningDialog() void AndroidSettingsWidget::manageAVD() { - QProcess *avdProcess = new QProcess(); - connect(this, &QObject::destroyed, avdProcess, &QObject::deleteLater); - connect(avdProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished), - avdProcess, &QObject::deleteLater); - - avdProcess->setProcessEnvironment(m_androidConfig.androidToolEnvironment().toProcessEnvironment()); - QString executable = m_androidConfig.androidToolPath().toString(); - QStringList arguments = QStringList("avd"); - - avdProcess->start(executable, arguments); + if (m_avdManager->avdManagerUiToolAvailable()) { + m_avdManager->launchAvdManagerUiTool(); + } else { + QMessageBox::warning(this, tr("AVD Manager Not Available"), + tr("AVD manager UI tool is not available in the installed SDK tools" + "(version %1). Use the command line tool \"avdmanager\" for " + "advanced AVD management.") + .arg(m_androidConfig.sdkToolsVersion().toString())); + } } diff --git a/src/plugins/android/androidsettingswidget.h b/src/plugins/android/androidsettingswidget.h index 3572ffef1a..50be7c1049 100644 --- a/src/plugins/android/androidsettingswidget.h +++ b/src/plugins/android/androidsettingswidget.h @@ -33,6 +33,8 @@ #include <QAbstractTableModel> #include <QFutureWatcher> +#include <memory> + QT_BEGIN_NAMESPACE class Ui_AndroidSettingsWidget; QT_END_NAMESPACE @@ -40,11 +42,13 @@ QT_END_NAMESPACE namespace Android { namespace Internal { +class AndroidAvdManager; + class AvdModel: public QAbstractTableModel { Q_OBJECT public: - void setAvdList(const QVector<AndroidDeviceInfo> &list); + void setAvdList(const AndroidDeviceInfoList &list); QString avdName(const QModelIndex &index) const; QModelIndex indexForAvdName(const QString &avdName) const; @@ -55,7 +59,7 @@ protected: int columnCount(const QModelIndex &parent = QModelIndex()) const; private: - QVector<AndroidDeviceInfo> m_list; + AndroidDeviceInfoList m_list; }; class AndroidSettingsWidget : public QWidget @@ -91,6 +95,7 @@ private: void checkGdbFinished(); void showGdbWarningDialog(); void updateAvds(); + void updateGradleBuildUi(); private: enum Mode { Sdk = 1, Ndk = 2, Java = 4, All = Sdk | Ndk | Java }; @@ -117,8 +122,9 @@ private: QFutureWatcher<QPair<QStringList, bool>> m_checkGdbWatcher; QStringList m_gdbCheckPaths; - QFutureWatcher<QVector<AndroidDeviceInfo>> m_virtualDevicesWatcher; + QFutureWatcher<AndroidDeviceInfoList> m_virtualDevicesWatcher; QString m_lastAddedAvd; + std::unique_ptr<AndroidAvdManager> m_avdManager; }; } // namespace Internal diff --git a/src/plugins/android/androidsettingswidget.ui b/src/plugins/android/androidsettingswidget.ui index 952dc444cc..338c73da04 100644 --- a/src/plugins/android/androidsettingswidget.ui +++ b/src/plugins/android/androidsettingswidget.ui @@ -237,20 +237,30 @@ </layout> </item> <item row="8" column="1"> - <widget class="QCheckBox" name="CreateKitCheckBox"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Automatically create kits for Android tool chains</string> + <layout class="QHBoxLayout" name="horizontalLayout_8"> + <property name="spacing"> + <number>2</number> </property> - <property name="checked"> - <bool>true</bool> + <property name="leftMargin"> + <number>0</number> </property> - </widget> + <item> + <widget class="QCheckBox" name="CreateKitCheckBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Automatically create kits for Android tool chains</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> </item> <item row="9" column="1"> <layout class="QHBoxLayout" name="horizontalLayout_2"> @@ -289,17 +299,59 @@ </layout> </item> <item row="10" column="1"> - <widget class="QCheckBox" name="UseGradleCheckBox"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <layout class="QHBoxLayout" name="horizontalLayout_7"> + <property name="spacing"> + <number>4</number> </property> - <property name="text"> - <string>Use Gradle instead of Ant</string> + <property name="leftMargin"> + <number>0</number> </property> - </widget> + <item> + <widget class="QCheckBox" name="UseGradleCheckBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Use Gradle instead of Ant (Ant builds are deprecated)</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="deprecatedInfoIconLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Gradle builds are forced from Android SDK tools version 25.3.0 onwards as Ant scripts are no longer available.</string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + </layout> </item> <item row="11" column="0"> <widget class="QLabel" name="AntLocationLabel"> diff --git a/src/plugins/android/androidtoolmanager.cpp b/src/plugins/android/androidtoolmanager.cpp new file mode 100644 index 0000000000..a6e61d96d9 --- /dev/null +++ b/src/plugins/android/androidtoolmanager.cpp @@ -0,0 +1,346 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#include "androidtoolmanager.h" + +#include "androidmanager.h" + +#include "utils/algorithm.h" +#include "utils/environment.h" +#include "utils/qtcassert.h" +#include "utils/runextensions.h" +#include "utils/synchronousprocess.h" + +#include <QLoggingCategory> + +namespace { +Q_LOGGING_CATEGORY(androidToolLog, "qtc.android.sdkManager") +} + +namespace Android { +namespace Internal { + +using namespace Utils; + +class AndroidToolOutputParser +{ +public: + void parseTargetListing(const QString &output, const FileName &sdkLocation, + SdkPlatformList *platformList); + + QList<SdkPlatform> m_installedPlatforms; +}; + +/*! + Runs the \c android tool located at \a toolPath with arguments \a args and environment \a + environment. Returns \c true for successful execution. Command's output is copied to \a + output. + */ +static bool androidToolCommand(Utils::FileName toolPath, const QStringList &args, + const Environment &environment, QString *output) +{ + QString androidToolPath = toolPath.toString(); + SynchronousProcess proc; + proc.setProcessEnvironment(environment.toProcessEnvironment()); + SynchronousProcessResponse response = proc.runBlocking(androidToolPath, args); + if (response.result == SynchronousProcessResponse::Finished) { + if (output) + *output = response.allOutput(); + return true; + } + return false; +} + +static QStringList cleanAndroidABIs(const QStringList &abis) +{ + QStringList res; + foreach (const QString &abi, abis) { + int index = abi.lastIndexOf(QLatin1Char('/')); + if (index == -1) + res << abi; + else + res << abi.mid(index + 1); + } + return res; +} + +AndroidToolManager::AndroidToolManager(const AndroidConfig &config) : + m_config(config), + m_parser(new AndroidToolOutputParser) +{ + +} + +AndroidToolManager::~AndroidToolManager() +{ + +} + +SdkPlatformList AndroidToolManager::availableSdkPlatforms() const +{ + SdkPlatformList list; + QString targetListing; + if (androidToolCommand(m_config.androidToolPath(), QStringList({"list", "target"}), + androidToolEnvironment(), &targetListing)) { + m_parser->parseTargetListing(targetListing, m_config.sdkLocation(), &list); + } else { + qCDebug(androidToolLog) << "Android tool target listing failed"; + } + return list; +} + +void AndroidToolManager::launchAvdManager() const +{ + QProcess::startDetached(m_config.androidToolPath().toString(), QStringList("avd")); +} + +QFuture<AndroidConfig::CreateAvdInfo> +AndroidToolManager::createAvd(AndroidConfig::CreateAvdInfo info) const +{ + return Utils::runAsync(&AndroidToolManager::createAvdImpl, info, + m_config.androidToolPath(), androidToolEnvironment()); +} + +bool AndroidToolManager::removeAvd(const QString &name) const +{ + SynchronousProcess proc; + proc.setTimeoutS(5); + proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment()); + SynchronousProcessResponse response + = proc.runBlocking(m_config.androidToolPath().toString(), + QStringList({"delete", "avd", "-n", name})); + return response.result == SynchronousProcessResponse::Finished && response.exitCode == 0; +} + +QFuture<AndroidDeviceInfoList> AndroidToolManager::androidVirtualDevicesFuture() const +{ + return Utils::runAsync(&AndroidToolManager::androidVirtualDevices, + m_config.androidToolPath(), m_config.sdkLocation(), + androidToolEnvironment()); +} + +Environment AndroidToolManager::androidToolEnvironment() const +{ + Environment env = Environment::systemEnvironment(); + Utils::FileName jdkLocation = m_config.openJDKLocation(); + if (!jdkLocation.isEmpty()) { + env.set(QLatin1String("JAVA_HOME"), jdkLocation.toUserOutput()); + Utils::FileName binPath = jdkLocation; + binPath.appendPath(QLatin1String("bin")); + env.prependOrSetPath(binPath.toUserOutput()); + } + return env; +} + +AndroidConfig::CreateAvdInfo AndroidToolManager::createAvdImpl(AndroidConfig::CreateAvdInfo info, + FileName androidToolPath, + Environment env) +{ + QProcess proc; + proc.setProcessEnvironment(env.toProcessEnvironment()); + QStringList arguments; + arguments << QLatin1String("create") << QLatin1String("avd") + << QLatin1String("-t") << info.target.name + << QLatin1String("-n") << info.name + << QLatin1String("-b") << info.abi; + if (info.sdcardSize > 0) + arguments << QLatin1String("-c") << QString::fromLatin1("%1M").arg(info.sdcardSize); + proc.start(androidToolPath.toString(), arguments); + if (!proc.waitForStarted()) { + info.error = tr("Could not start process \"%1 %2\"") + .arg(androidToolPath.toString(), arguments.join(QLatin1Char(' '))); + return info; + } + QTC_CHECK(proc.state() == QProcess::Running); + proc.write(QByteArray("yes\n")); // yes to "Do you wish to create a custom hardware profile" + + QByteArray question; + while (true) { + proc.waitForReadyRead(500); + question += proc.readAllStandardOutput(); + if (question.endsWith(QByteArray("]:"))) { + // truncate to last line + int index = question.lastIndexOf(QByteArray("\n")); + if (index != -1) + question = question.mid(index); + if (question.contains("hw.gpu.enabled")) + proc.write(QByteArray("yes\n")); + else + proc.write(QByteArray("\n")); + question.clear(); + } + + if (proc.state() != QProcess::Running) + break; + } + QTC_CHECK(proc.state() == QProcess::NotRunning); + + QString errorOutput = QString::fromLocal8Bit(proc.readAllStandardError()); + // The exit code is always 0, so we need to check stderr + // For now assume that any output at all indicates a error + if (!errorOutput.isEmpty()) { + info.error = errorOutput; + } + + return info; +} + +AndroidDeviceInfoList AndroidToolManager::androidVirtualDevices(const Utils::FileName &androidTool, + const FileName &sdkLocationPath, + const Environment &environment) +{ + AndroidDeviceInfoList devices; + QString output; + if (!androidToolCommand(androidTool, QStringList({"list", "avd"}), environment, &output)) + return devices; + + QStringList avds = output.split('\n'); + if (avds.empty()) + return devices; + + while (avds.first().startsWith(QLatin1String("* daemon"))) + avds.removeFirst(); // remove the daemon logs + avds.removeFirst(); // remove "List of devices attached" header line + + bool nextLineIsTargetLine = false; + + AndroidDeviceInfo dev; + for (int i = 0; i < avds.size(); i++) { + QString line = avds.at(i); + if (!line.contains(QLatin1String("Name:"))) + continue; + + int index = line.indexOf(QLatin1Char(':')) + 2; + if (index >= line.size()) + break; + dev.avdname = line.mid(index).trimmed(); + dev.sdk = -1; + dev.cpuAbi.clear(); + ++i; + for (; i < avds.size(); ++i) { + line = avds.at(i); + if (line.contains(QLatin1String("---------"))) + break; + + if (line.contains(QLatin1String("Target:")) || nextLineIsTargetLine) { + if (line.contains(QLatin1String("Google APIs"))) { + nextLineIsTargetLine = true; + continue; + } + + nextLineIsTargetLine = false; + + int lastIndex = line.lastIndexOf(QLatin1Char(' ')); + if (lastIndex == -1) // skip line + break; + QString tmp = line.mid(lastIndex).remove(QLatin1Char(')')).trimmed(); + Utils::FileName platformPath = sdkLocationPath; + platformPath.appendPath(QString("/platforms/android-%1").arg(tmp)); + dev.sdk = AndroidManager::findApiLevel(platformPath); + } + if (line.contains(QLatin1String("Tag/ABI:"))) { + int lastIndex = line.lastIndexOf(QLatin1Char('/')) + 1; + if (lastIndex >= line.size()) + break; + dev.cpuAbi = QStringList(line.mid(lastIndex)); + } else if (line.contains(QLatin1String("ABI:"))) { + int lastIndex = line.lastIndexOf(QLatin1Char(' ')) + 1; + if (lastIndex >= line.size()) + break; + dev.cpuAbi = QStringList(line.mid(lastIndex).trimmed()); + } + } + // armeabi-v7a devices can also run armeabi code + if (dev.cpuAbi == QStringList("armeabi-v7a")) + dev.cpuAbi << QLatin1String("armeabi"); + dev.state = AndroidDeviceInfo::OkState; + dev.type = AndroidDeviceInfo::Emulator; + if (dev.cpuAbi.isEmpty() || dev.sdk == -1) + continue; + devices.push_back(dev); + } + Utils::sort(devices); + + return devices; +} + +void AndroidToolOutputParser::parseTargetListing(const QString &output, + const Utils::FileName &sdkLocation, + SdkPlatformList *platformList) +{ + if (!platformList) + return; + + auto addSystemImage = [](const QStringList& abiList, SdkPlatform &platform) { + foreach (auto imageAbi, abiList) { + SystemImage image; + image.abiName = imageAbi; + image.apiLevel = platform.apiLevel; + platform.systemImages.append(image); + } + }; + + SdkPlatform platform; + QStringList abiList; + foreach (const QString &l, output.split('\n')) { + const QString line = l.trimmed(); + if (line.startsWith(QLatin1String("id:")) && line.contains(QLatin1String("android-"))) { + int index = line.indexOf(QLatin1String("\"android-")); + if (index == -1) + continue; + QString androidTarget = line.mid(index + 1, line.length() - index - 2); + const QString tmp = androidTarget.mid(androidTarget.lastIndexOf(QLatin1Char('-')) + 1); + Utils::FileName platformPath = sdkLocation; + platformPath.appendPath(QString("/platforms/android-%1").arg(tmp)); + platform.installedLocation = platformPath; + platform.apiLevel = AndroidManager::findApiLevel(platformPath); + } else if (line.startsWith(QLatin1String("Name:"))) { + platform.name = line.mid(6); + } else if (line.startsWith(QLatin1String("Tag/ABIs :"))) { + abiList = cleanAndroidABIs(line.mid(10).trimmed().split(QLatin1String(", "))); + } else if (line.startsWith(QLatin1String("ABIs"))) { + abiList = cleanAndroidABIs(line.mid(6).trimmed().split(QLatin1String(", "))); + } else if (line.startsWith(QLatin1String("---")) || line.startsWith(QLatin1String("==="))) { + if (platform.apiLevel == -1) + continue; + + addSystemImage(abiList, platform); + *platformList << platform; + + platform = SdkPlatform(); + abiList.clear(); + } + } + + // The last parsed Platform. + if (platform.apiLevel != -1) { + addSystemImage(abiList, platform); + *platformList << platform; + } + + Utils::sort(*platformList); +} + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/androidtoolmanager.h b/src/plugins/android/androidtoolmanager.h new file mode 100644 index 0000000000..befb095b92 --- /dev/null +++ b/src/plugins/android/androidtoolmanager.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ +#pragma once + +#include "utils/fileutils.h" +#include "androidconfigurations.h" + +#include <QStringList> + +#include <memory> + +namespace Android { +class AndroidConfig; + +namespace Internal { + +class AndroidToolOutputParser; +/*! + Wraps the \c android tool's usage. The tool itself is deprecated since SDK tools version 25.3.0. + */ +class AndroidToolManager +{ + Q_DECLARE_TR_FUNCTIONS(AndroidToolManager) + +public: + AndroidToolManager(const AndroidConfig &config); + ~AndroidToolManager(); + + SdkPlatformList availableSdkPlatforms() const; + void launchAvdManager() const; + + QFuture<AndroidConfig::CreateAvdInfo> createAvd(AndroidConfig::CreateAvdInfo info) const; + bool removeAvd(const QString &name) const; + QFuture<AndroidDeviceInfoList> androidVirtualDevicesFuture() const; + +// Helper methods +private: + Utils::Environment androidToolEnvironment() const; + static AndroidConfig::CreateAvdInfo createAvdImpl(AndroidConfig::CreateAvdInfo info, + Utils::FileName androidToolPath, Utils::Environment env); + static AndroidDeviceInfoList androidVirtualDevices(const Utils::FileName &androidTool, + const Utils::FileName &sdkLlocationPath, + const Utils::Environment &environment); +private: + const AndroidConfig &m_config; + std::unique_ptr<AndroidToolOutputParser> m_parser; +}; + +} // namespace Internal +} // namespace Android diff --git a/src/plugins/android/avddialog.cpp b/src/plugins/android/avddialog.cpp index 9bfea8ef21..eb7da93e90 100644 --- a/src/plugins/android/avddialog.cpp +++ b/src/plugins/android/avddialog.cpp @@ -26,6 +26,7 @@ #include "avddialog.h" #include "androidconfigurations.h" +#include <utils/algorithm.h> #include <utils/tooltip/tooltip.h> #include <utils/utilsicons.h> @@ -67,12 +68,12 @@ AvdDialog::AvdDialog(int minApiLevel, const QString &targetArch, const AndroidCo bool AvdDialog::isValid() const { - return !name().isEmpty() && !target().isEmpty() && !abi().isEmpty(); + return !name().isEmpty() && target().isValid() && !abi().isEmpty(); } -QString AvdDialog::target() const +SdkPlatform AvdDialog::target() const { - return m_avdDialog.targetComboBox->currentText(); + return m_avdDialog.targetComboBox->currentData().value<SdkPlatform>(); } QString AvdDialog::name() const @@ -92,15 +93,23 @@ int AvdDialog::sdcardSize() const void AvdDialog::updateApiLevelComboBox() { - QList<SdkPlatform> filteredList; - QList<SdkPlatform> platforms = m_config->sdkTargets(m_minApiLevel); - foreach (const SdkPlatform &platform, platforms) { - if (platform.abis.contains(abi())) - filteredList << platform; - } + SdkPlatformList filteredList; + SdkPlatformList platforms = m_config->sdkTargets(m_minApiLevel); + + QString selectedAbi = abi(); + auto hasAbi = [selectedAbi](const SystemImage &image) { + return image.isValid() && (image.abiName == selectedAbi); + }; + + filteredList = Utils::filtered(platforms, [hasAbi](const SdkPlatform &platform) { + return Utils::anyOf(platform.systemImages,hasAbi); + }); m_avdDialog.targetComboBox->clear(); - m_avdDialog.targetComboBox->addItems(AndroidConfig::apiLevelNamesFor(filteredList)); + foreach (const SdkPlatform &platform, filteredList) { + m_avdDialog.targetComboBox->addItem(AndroidConfig::apiLevelNameFor(platform), + QVariant::fromValue<SdkPlatform>(platform)); + } if (platforms.isEmpty()) { m_avdDialog.warningIcon->setVisible(true); diff --git a/src/plugins/android/avddialog.h b/src/plugins/android/avddialog.h index 950285aadc..d22e6af64f 100644 --- a/src/plugins/android/avddialog.h +++ b/src/plugins/android/avddialog.h @@ -32,6 +32,7 @@ namespace Android { class AndroidConfig; +class SdkPlatform; namespace Internal { @@ -42,7 +43,7 @@ public: explicit AvdDialog(int minApiLevel, const QString &targetArch, const AndroidConfig *config, QWidget *parent = 0); - QString target() const; + Android::SdkPlatform target() const; QString name() const; QString abi() const; int sdcardSize() const; diff --git a/src/plugins/autotest/testconfiguration.cpp b/src/plugins/autotest/testconfiguration.cpp index 9ecd8d97ad..880c42457a 100644 --- a/src/plugins/autotest/testconfiguration.cpp +++ b/src/plugins/autotest/testconfiguration.cpp @@ -130,6 +130,7 @@ void TestConfiguration::completeTestInformation(int runMode) m_executableFile = exeString; m_project = project; m_guessedConfiguration = true; + m_guessedFrom = rc->displayName(); if (runMode == TestRunner::Debug) m_runConfig = new TestRunConfiguration(rc->target(), this); } @@ -203,11 +204,6 @@ void TestConfiguration::setProject(Project *project) m_project = project; } -void TestConfiguration::setGuessedConfiguration(bool guessed) -{ - m_guessedConfiguration = guessed; -} - QString TestConfiguration::executableFilePath() const { if (m_executableFile.isEmpty()) diff --git a/src/plugins/autotest/testconfiguration.h b/src/plugins/autotest/testconfiguration.h index d694df14cd..e220ea2d36 100644 --- a/src/plugins/autotest/testconfiguration.h +++ b/src/plugins/autotest/testconfiguration.h @@ -66,7 +66,6 @@ public: void setDisplayName(const QString &displayName); void setEnvironment(const Utils::Environment &env); void setProject(ProjectExplorer::Project *project); - void setGuessedConfiguration(bool guessed); QStringList testCases() const { return m_testCases; } int testCaseCount() const { return m_testCaseCount; } @@ -77,7 +76,9 @@ public: Utils::Environment environment() const { return m_environment; } ProjectExplorer::Project *project() const { return m_project.data(); } TestRunConfiguration *runConfiguration() const { return m_runConfig; } - bool guessedConfiguration() const { return m_guessedConfiguration; } + bool isGuessed() const { return m_guessedConfiguration; } + QString runConfigDisplayName() const { return m_guessedConfiguration ? m_guessedFrom + : m_displayName; } virtual TestOutputReader *outputReader(const QFutureInterface<TestResultPtr> &fi, QProcess *app) const = 0; @@ -91,6 +92,7 @@ private: QString m_workingDir; QString m_buildDir; QString m_displayName; + QString m_guessedFrom; Utils::Environment m_environment; QPointer<ProjectExplorer::Project> m_project; bool m_guessedConfiguration = false; diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp index f02262f891..6d406943e0 100644 --- a/src/plugins/autotest/testrunner.cpp +++ b/src/plugins/autotest/testrunner.cpp @@ -43,6 +43,7 @@ #include <utils/outputformat.h> #include <utils/runextensions.h> +#include <utils/hostosinfo.h> #include <QFuture> #include <QFutureInterface> @@ -96,6 +97,28 @@ void TestRunner::setSelectedTests(const QList<TestConfiguration *> &selected) m_selectedTests = selected; } +static QString processInformation(const QProcess &proc) +{ + QString information("\nCommand line: " + proc.program() + ' ' + proc.arguments().join(' ')); + QStringList important = { "PATH" }; + if (Utils::HostOsInfo::isLinuxHost()) + important.append("LD_LIBRARY_PATH"); + else if (Utils::HostOsInfo::isMacHost()) + important.append({ "DYLD_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH" }); + const QProcessEnvironment &environment = proc.processEnvironment(); + for (const QString &var : important) + information.append('\n' + var + ": " + environment.value(var)); + return information; +} + +static QString rcInfo(const TestConfiguration * const config) +{ + QString info = '\n' + TestRunner::tr("Run configuration:") + ' '; + if (config->isGuessed()) + info += TestRunner::tr("guessed from"); + return info + " \"" + config->runConfigDisplayName() + '"'; +} + static void performTestRun(QFutureInterface<TestResultPtr> &futureInterface, const QList<TestConfiguration *> selectedTests, const TestSettings &settings) @@ -108,11 +131,14 @@ static void performTestRun(QFutureInterface<TestResultPtr> &futureInterface, config->completeTestInformation(TestRunner::Run); if (config->project()) { testCaseCount += config->testCaseCount(); - if (!omitRunConfigWarnings && config->guessedConfiguration()) { - futureInterface.reportResult(TestResultPtr(new FaultyTestResult(Result::MessageWarn, - TestRunner::tr("Project's run configuration was guessed for \"%1\".\n" - "This might cause trouble during execution." - ).arg(config->displayName())))); + if (!omitRunConfigWarnings && config->isGuessed()) { + QString message = TestRunner::tr( + "Project's run configuration was guessed for \"%1\".\n" + "This might cause trouble during execution.\n" + "(guessed from \"%2\")"); + message = message.arg(config->displayName()).arg(config->runConfigDisplayName()); + futureInterface.reportResult( + TestResultPtr(new FaultyTestResult(Result::MessageWarn, message))); } } else { futureInterface.reportResult(TestResultPtr(new FaultyTestResult(Result::MessageWarn, @@ -173,11 +199,15 @@ static void performTestRun(QFutureInterface<TestResultPtr> &futureInterface, } } else { futureInterface.reportResult(TestResultPtr(new FaultyTestResult(Result::MessageFatal, - TestRunner::tr("Failed to start test for project \"%1\".").arg(testConfiguration->displayName())))); + TestRunner::tr("Failed to start test for project \"%1\".") + .arg(testConfiguration->displayName()) + processInformation(testProcess) + + rcInfo(testConfiguration)))); } if (testProcess.exitStatus() == QProcess::CrashExit) { futureInterface.reportResult(TestResultPtr(new FaultyTestResult(Result::MessageFatal, - TestRunner::tr("Test for project \"%1\" crashed.").arg(testConfiguration->displayName())))); + TestRunner::tr("Test for project \"%1\" crashed.") + .arg(testConfiguration->displayName()) + processInformation(testProcess) + + rcInfo(testConfiguration)))); } if (canceledByTimeout) { diff --git a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp index a4a733d0cd..7240fab3a5 100644 --- a/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp +++ b/src/plugins/autotoolsprojectmanager/autotoolsproject.cpp @@ -75,6 +75,7 @@ AutotoolsProject::AutotoolsProject(const Utils::FileName &fileName) : setId(Constants::AUTOTOOLS_PROJECT_ID); setProjectContext(Core::Context(Constants::PROJECT_CONTEXT)); setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); + setDisplayName(projectDirectory().fileName()); } AutotoolsProject::~AutotoolsProject() @@ -90,11 +91,6 @@ AutotoolsProject::~AutotoolsProject() } } -QString AutotoolsProject::displayName() const -{ - return projectFilePath().toFileInfo().absoluteDir().dirName(); -} - QString AutotoolsProject::defaultBuildDirectory(const QString &projectPath) { return QFileInfo(projectPath).absolutePath(); diff --git a/src/plugins/autotoolsprojectmanager/autotoolsproject.h b/src/plugins/autotoolsprojectmanager/autotoolsproject.h index 3ebaeb763a..2183ee92b7 100644 --- a/src/plugins/autotoolsprojectmanager/autotoolsproject.h +++ b/src/plugins/autotoolsprojectmanager/autotoolsproject.h @@ -57,7 +57,6 @@ public: explicit AutotoolsProject(const Utils::FileName &fileName); ~AutotoolsProject() override; - QString displayName() const override; static QString defaultBuildDirectory(const QString &projectPath); QStringList buildTargets() const; diff --git a/src/plugins/autotoolsprojectmanager/autotoolsprojectnode.cpp b/src/plugins/autotoolsprojectmanager/autotoolsprojectnode.cpp index e98246ed52..793d63a762 100644 --- a/src/plugins/autotoolsprojectmanager/autotoolsprojectnode.cpp +++ b/src/plugins/autotoolsprojectmanager/autotoolsprojectnode.cpp @@ -42,9 +42,3 @@ bool AutotoolsProjectNode::showInSimpleTree() const { return true; } - -QList<ProjectAction> AutotoolsProjectNode::supportedActions(Node *node) const -{ - Q_UNUSED(node); - return QList<ProjectAction>(); -} diff --git a/src/plugins/autotoolsprojectmanager/autotoolsprojectnode.h b/src/plugins/autotoolsprojectmanager/autotoolsprojectnode.h index 088e007a4a..a60479eb8c 100644 --- a/src/plugins/autotoolsprojectmanager/autotoolsprojectnode.h +++ b/src/plugins/autotoolsprojectmanager/autotoolsprojectnode.h @@ -51,7 +51,6 @@ public: AutotoolsProjectNode(const Utils::FileName &projectDirectory); bool showInSimpleTree() const override; - QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const override; }; } // namespace Internal diff --git a/src/plugins/beautifier/beautifierplugin.cpp b/src/plugins/beautifier/beautifierplugin.cpp index ad2598152c..4120643882 100644 --- a/src/plugins/beautifier/beautifierplugin.cpp +++ b/src/plugins/beautifier/beautifierplugin.cpp @@ -215,7 +215,7 @@ void BeautifierPlugin::extensionsInitialized() addAutoReleasedObject(object); } - m_generalSettings = new GeneralSettings; + m_generalSettings.reset(new GeneralSettings); auto settingsPage = new GeneralOptionsPage(m_generalSettings, toolIds, this); addAutoReleasedObject(settingsPage); diff --git a/src/plugins/beautifier/beautifierplugin.h b/src/plugins/beautifier/beautifierplugin.h index 8fab2a4e03..b75f34bab0 100644 --- a/src/plugins/beautifier/beautifierplugin.h +++ b/src/plugins/beautifier/beautifierplugin.h @@ -31,6 +31,7 @@ #include <QPlainTextEdit> #include <QPointer> +#include <QSharedPointer> namespace Core { class IDocument; @@ -86,7 +87,7 @@ public: private: void updateActions(Core::IEditor *editor = nullptr); QList<BeautifierAbstractTool *> m_tools; - GeneralSettings *m_generalSettings = nullptr; + QSharedPointer<GeneralSettings> m_generalSettings; QHash<QObject*, QMetaObject::Connection> m_autoFormatConnections; void formatEditor(TextEditor::TextEditorWidget *editor, const Command &command, int startPos = -1, int endPos = 0); diff --git a/src/plugins/beautifier/generaloptionspage.cpp b/src/plugins/beautifier/generaloptionspage.cpp index b4c620054e..4f160e320f 100644 --- a/src/plugins/beautifier/generaloptionspage.cpp +++ b/src/plugins/beautifier/generaloptionspage.cpp @@ -36,7 +36,7 @@ namespace Beautifier { namespace Internal { -GeneralOptionsPageWidget::GeneralOptionsPageWidget(GeneralSettings *settings, +GeneralOptionsPageWidget::GeneralOptionsPageWidget(const QSharedPointer<GeneralSettings> &settings, const QStringList &toolIds, QWidget *parent) : QWidget(parent), ui(new Ui::GeneralOptionsPage), @@ -73,8 +73,8 @@ void GeneralOptionsPageWidget::apply(bool *autoFormatChanged) m_settings->save(); } -GeneralOptionsPage::GeneralOptionsPage(GeneralSettings *settings, const QStringList &toolIds, - QObject *parent) : +GeneralOptionsPage::GeneralOptionsPage(const QSharedPointer<GeneralSettings> &settings, + const QStringList &toolIds, QObject *parent) : IOptionsPage(parent), m_settings(settings), m_toolIds(toolIds) diff --git a/src/plugins/beautifier/generaloptionspage.h b/src/plugins/beautifier/generaloptionspage.h index 2f6d79dd90..3500e7c2db 100644 --- a/src/plugins/beautifier/generaloptionspage.h +++ b/src/plugins/beautifier/generaloptionspage.h @@ -28,6 +28,7 @@ #include <coreplugin/dialogs/ioptionspage.h> #include <QPointer> +#include <QSharedPointer> #include <QWidget> namespace Beautifier { @@ -42,15 +43,15 @@ class GeneralOptionsPageWidget : public QWidget Q_OBJECT public: - explicit GeneralOptionsPageWidget(GeneralSettings *settings, const QStringList &toolIds, - QWidget *parent = nullptr); + explicit GeneralOptionsPageWidget(const QSharedPointer<GeneralSettings> &settings, + const QStringList &toolIds, QWidget *parent = nullptr); virtual ~GeneralOptionsPageWidget(); void restore(); void apply(bool *autoFormatChanged); private: Ui::GeneralOptionsPage *ui; - GeneralSettings *m_settings; + QSharedPointer<GeneralSettings> m_settings; }; class GeneralOptionsPage : public Core::IOptionsPage @@ -58,8 +59,8 @@ class GeneralOptionsPage : public Core::IOptionsPage Q_OBJECT public: - explicit GeneralOptionsPage(GeneralSettings *settings, const QStringList &toolIds, - QObject *parent = nullptr); + explicit GeneralOptionsPage(const QSharedPointer<GeneralSettings> &settings, + const QStringList &toolIds, QObject *parent = nullptr); QWidget *widget() override; void apply() override; void finish() override; @@ -69,7 +70,7 @@ signals: private: QPointer<GeneralOptionsPageWidget> m_widget; - GeneralSettings *m_settings; + QSharedPointer<GeneralSettings> m_settings; QStringList m_toolIds; }; diff --git a/src/plugins/classview/classviewparser.cpp b/src/plugins/classview/classviewparser.cpp index 901edacf91..8d4663819a 100644 --- a/src/plugins/classview/classviewparser.cpp +++ b/src/plugins/classview/classviewparser.cpp @@ -101,7 +101,7 @@ public: CPlusPlus::Overview overview; //! timer - QPointer<QTimer> timer; + QTimer timer; // documents //! Documents read write lock @@ -161,16 +161,14 @@ Parser::Parser(QObject *parent) : QObject(parent), d(new ParserPrivate()) { - d->timer = new QTimer(this); - d->timer->setObjectName(QLatin1String("ClassViewParser::timer")); - d->timer->setSingleShot(true); + d->timer.setSingleShot(true); // connect signal/slots // internal data reset connect(this, &Parser::resetDataDone, this, &Parser::onResetDataDone, Qt::QueuedConnection); // timer for emitting changes - connect(d->timer.data(), &QTimer::timeout, this, &Parser::requestCurrentState, Qt::QueuedConnection); + connect(&d->timer, &QTimer::timeout, this, &Parser::requestCurrentState, Qt::QueuedConnection); } /*! @@ -294,11 +292,12 @@ ParserTreeItem::ConstPtr Parser::parse() item = ParserTreeItem::Ptr(new ParserTreeItem()); if (d->flatMode) - addFlatTree(item, prj->rootProjectNode()); + addFlatTree(item, prj); else - addProjectNode(item, prj->rootProjectNode()); + addProjectTree(item, prj); + + item->setIcon(prj->containerNode()->icon()); - item->setIcon(prj->rootProjectNode()->icon()); rootItem->appendChild(item, inf); } @@ -315,7 +314,7 @@ ParserTreeItem::ConstPtr Parser::parse() */ void Parser::addProject(const ParserTreeItem::Ptr &item, const QStringList &fileList, - const QString &projectId) + const QString &projectId) { // recalculate cache tree if needed ParserTreeItem::Ptr prj(getCachedOrParseProjectTree(fileList, projectId)); @@ -542,10 +541,8 @@ void Parser::parseDocument(const CPlusPlus::Document::Ptr &doc) getParseDocumentTree(doc); - QTC_ASSERT(d->timer, return); - - if (!d->timer->isActive()) - d->timer->start(400); //! Delay in msecs before an update + if (!d->timer.isActive()) + d->timer.start(400); //! Delay in msecs before an update return; } @@ -688,7 +685,7 @@ void Parser::requestCurrentState() void Parser::emitCurrentTree() { // stop timer if it is active right now - d->timer->stop(); + d->timer.stop(); d->rootItemLocker.lockForWrite(); d->rootItem = parse(); @@ -703,110 +700,69 @@ void Parser::emitCurrentTree() } /*! - Generates a project node file list for the root node \a node. -*/ - -QStringList Parser::projectNodeFileList(const FolderNode *folderNode) const -{ - QStringList list; - folderNode->forEachNode( - [&](FileNode *node) { - if (!node->isGenerated()) - list.append(node->filePath().toString()); - }, - {}, - [&](const FolderNode *node) { - return node->nodeType() == NodeType::Folder || node->nodeType() == NodeType::VirtualFolder; - } - ); - return list; -} - -/*! Generates projects like the Project Explorer. \a item specifies the item and \a node specifies the root node. Returns a list of projects which were added to the item. */ -QStringList Parser::addProjectNode(const ParserTreeItem::Ptr &item, const ProjectNode *node) +QStringList Parser::addProjectTree(const ParserTreeItem::Ptr &item, const Project *project) { QStringList projectList; - if (!node) + if (!project) return projectList; - const QString nodePath = node->filePath().toString(); + const QString projectPath = project->projectFilePath().toString(); // our own files QStringList fileList; - CitCachedPrjFileLists cit = d->cachedPrjFileLists.constFind(nodePath); + CitCachedPrjFileLists cit = d->cachedPrjFileLists.constFind(projectPath); // try to improve parsing speed by internal cache if (cit != d->cachedPrjFileLists.constEnd()) { fileList = cit.value(); } else { - fileList = projectNodeFileList(node); - d->cachedPrjFileLists[nodePath] = fileList; + fileList = project->files(Project::SourceFiles); + d->cachedPrjFileLists[projectPath] = fileList; } if (fileList.count() > 0) { - addProject(item, fileList, node->filePath().toString()); - projectList << node->filePath().toString(); - } - - // subnodes - for (const Node *n : node->nodes()) { - if (const ProjectNode *project = n->asProjectNode()) { - ParserTreeItem::Ptr itemPrj(new ParserTreeItem()); - SymbolInformation information(project->displayName(), project->filePath().toString()); - - projectList += addProjectNode(itemPrj, project); - - itemPrj->setIcon(project->icon()); - - // append child if item is not null and there is at least 1 child - if (!item.isNull() && itemPrj->childCount() > 0) - item->appendChild(itemPrj, information); - } + addProject(item, fileList, projectPath); + projectList << projectPath; } return projectList; } -QStringList Parser::getAllFiles(const ProjectNode *node) +QStringList Parser::getAllFiles(const Project *project) { QStringList fileList; - if (!node) + if (!project) return fileList; - const QString nodePath = node->filePath().toString(); + const QString nodePath = project->projectFilePath().toString(); CitCachedPrjFileLists cit = d->cachedPrjFileLists.constFind(nodePath); // try to improve parsing speed by internal cache if (cit != d->cachedPrjFileLists.constEnd()) { fileList = cit.value(); } else { - fileList = projectNodeFileList(node); + fileList = project->files(Project::SourceFiles); d->cachedPrjFileLists[nodePath] = fileList; } - // subnodes - - for (const Node *n : node->nodes()) - if (const ProjectNode *project = n->asProjectNode()) - fileList += getAllFiles(project); return fileList; } -void Parser::addFlatTree(const ParserTreeItem::Ptr &item, const ProjectNode *node) +void Parser::addFlatTree(const ParserTreeItem::Ptr &item, const Project *project) { - if (!node) + if (!project) return; - QStringList fileList = getAllFiles(node); + QStringList fileList = getAllFiles(project); fileList.removeDuplicates(); if (fileList.count() > 0) { - addProject(item, fileList, node->filePath().toString()); + addProject(item, fileList, project->projectFilePath().toString()); } } diff --git a/src/plugins/classview/classviewparser.h b/src/plugins/classview/classviewparser.h index cb90e4e8c7..918397b3c4 100644 --- a/src/plugins/classview/classviewparser.h +++ b/src/plugins/classview/classviewparser.h @@ -111,13 +111,9 @@ protected: ParserTreeItem::ConstPtr findItemByRoot(const QStandardItem *item, bool skipRoot = false) const; - QStringList addProjectNode(const ParserTreeItem::Ptr &item, - const ProjectExplorer::ProjectNode *node); - QStringList getAllFiles(const ProjectExplorer::ProjectNode *node); - void addFlatTree(const ParserTreeItem::Ptr &item, - const ProjectExplorer::ProjectNode *node); - - QStringList projectNodeFileList(const ProjectExplorer::FolderNode *node) const; + QStringList addProjectTree(const ParserTreeItem::Ptr &item, const ProjectExplorer::Project *project); + QStringList getAllFiles(const ProjectExplorer::Project *project); + void addFlatTree(const ParserTreeItem::Ptr &item, const ProjectExplorer::Project *project); private: //! Private class data pointer diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp index c5beb51b43..f4ba55d093 100644 --- a/src/plugins/clearcase/clearcaseplugin.cpp +++ b/src/plugins/clearcase/clearcaseplugin.cpp @@ -257,7 +257,7 @@ FileStatus::Status ClearCasePlugin::getFileStatus(const QString &fileName) const const QString absFile = viewRootDir.absoluteFilePath( QDir::fromNativeSeparators(buffer.left(atatpos))); - QTC_CHECK(QFile(absFile).exists()); + QTC_CHECK(QFileInfo::exists(absFile)); QTC_CHECK(!absFile.isEmpty()); // "cleartool ls" of a derived object looks like this: @@ -274,7 +274,7 @@ FileStatus::Status ClearCasePlugin::getFileStatus(const QString &fileName) const else return FileStatus::CheckedIn; } else { - QTC_CHECK(QFile(fileName).exists()); + QTC_CHECK(QFileInfo::exists(fileName)); QTC_CHECK(!fileName.isEmpty()); return FileStatus::NotManaged; } diff --git a/src/plugins/clearcase/clearcasesync.cpp b/src/plugins/clearcase/clearcasesync.cpp index 8f8262ee7c..2a9c16dfd7 100644 --- a/src/plugins/clearcase/clearcasesync.cpp +++ b/src/plugins/clearcase/clearcasesync.cpp @@ -92,7 +92,7 @@ void ClearCaseSync::processCleartoolLsLine(const QDir &viewRootDir, const QStrin const QString absFile = viewRootDir.absoluteFilePath( QDir::fromNativeSeparators(buffer.left(atatpos))); - QTC_CHECK(QFile(absFile).exists()); + QTC_CHECK(QFileInfo::exists(absFile)); QTC_CHECK(!absFile.isEmpty()); QString ccState; diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 6db04c5287..fbebafb9d5 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -149,10 +149,7 @@ void CMakeBuildConfiguration::ctor() emit dataAvailable(); }); connect(m_buildDirManager.get(), &BuildDirManager::errorOccured, - this, [this, project](const QString &msg) { - project->updateProjectData(this); - setError(msg); - }); + this, &CMakeBuildConfiguration::setError); connect(m_buildDirManager.get(), &BuildDirManager::configurationStarted, this, [this, project]() { project->handleParsingStarted(); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.cpp index a13a7e57f9..f77acdac10 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsettingswidget.cpp @@ -258,6 +258,8 @@ CMakeBuildSettingsWidget::CMakeBuildSettingsWidget(CMakeBuildConfiguration *bc) updateFromKit(); connect(m_buildConfiguration->target(), &ProjectExplorer::Target::kitChanged, this, &CMakeBuildSettingsWidget::updateFromKit); + connect(m_buildConfiguration, &CMakeBuildConfiguration::enabledChanged, + this, [this]() { setError(m_buildConfiguration->disabledReason()); }); } void CMakeBuildSettingsWidget::setError(const QString &message) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp index 24ce40dcaa..f92f129c09 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp @@ -444,19 +444,26 @@ QString CMakeBuildStepConfigWidget::displayName() const return tr("Build", "CMakeProjectManager::CMakeBuildStepConfigWidget display name."); } -void CMakeBuildStepConfigWidget::buildTargetsChanged() +static void createSpecialItem(const QString &text, const QString &data, QListWidget *parent) { - const bool wasBlocked = m_buildTargetsList->blockSignals(true); - m_buildTargetsList->clear(); + auto item = new QListWidgetItem(text, parent); - auto item = new QListWidgetItem(tr(ADD_RUNCONFIGURATION_TEXT), m_buildTargetsList); - - item->setData(Qt::UserRole, QString::fromLatin1(ADD_RUNCONFIGURATION_TEXT)); + item->setData(Qt::UserRole, data); QFont f; f.setItalic(true); item->setFont(f); +} + +void CMakeBuildStepConfigWidget::buildTargetsChanged() +{ + const bool wasBlocked = m_buildTargetsList->blockSignals(true); + m_buildTargetsList->clear(); + + createSpecialItem(tr(ADD_RUNCONFIGURATION_TEXT), ADD_RUNCONFIGURATION_TEXT, m_buildTargetsList); + createSpecialItem(tr("all"), "all", m_buildTargetsList); + createSpecialItem(tr("clean"), "clean", m_buildTargetsList); - CMakeProject *pro = static_cast<CMakeProject *>(m_buildStep->project()); + auto pro = static_cast<CMakeProject *>(m_buildStep->project()); QStringList targetList = pro->buildTargetTitles(); targetList.sort(); diff --git a/src/plugins/cmakeprojectmanager/cmakecbpparser.cpp b/src/plugins/cmakeprojectmanager/cmakecbpparser.cpp index 350950388f..591768f515 100644 --- a/src/plugins/cmakeprojectmanager/cmakecbpparser.cpp +++ b/src/plugins/cmakeprojectmanager/cmakecbpparser.cpp @@ -180,15 +180,6 @@ bool CMakeCbpParser::parseCbpFile(CMakeTool::PathMapper mapper, const FileName & fi.close(); - // There is always a clean target: - CMakeBuildTarget cleanTarget; - cleanTarget.title = "clean"; - cleanTarget.targetType = UtilityType; - cleanTarget.workingDirectory = m_buildDirectory; - cleanTarget.sourceDirectory = m_sourceDirectory; - - m_buildTargets.append(cleanTarget); - return true; } return false; diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.cpp b/src/plugins/cmakeprojectmanager/cmakeproject.cpp index 4bf0f0dcdc..b11480eb2c 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeproject.cpp @@ -79,6 +79,7 @@ CMakeProject::CMakeProject(const FileName &fileName) : Project(Constants::CMAKEM setId(CMakeProjectManager::Constants::CMAKEPROJECT_ID); setProjectContext(Core::Context(CMakeProjectManager::Constants::PROJECTCONTEXT)); setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); + setDisplayName(projectDirectory().fileName()); connect(this, &CMakeProject::activeTargetChanged, this, &CMakeProject::handleActiveTargetChanged); connect(&m_treeScanner, &TreeScanner::finished, this, &CMakeProject::handleTreeScanningFinished); @@ -145,8 +146,10 @@ void CMakeProject::updateProjectData(CMakeBuildConfiguration *bc) Kit *k = t->kit(); auto newRoot = bc->generateProjectTree(m_allFiles); - if (newRoot) + if (newRoot) { setRootProjectNode(newRoot); + setDisplayName(newRoot->displayName()); + } updateApplicationAndDeploymentTargets(); updateTargetRunConfigurations(t); @@ -296,12 +299,6 @@ bool CMakeProject::hasBuildTarget(const QString &title) const return anyOf(buildTargets(), [title](const CMakeBuildTarget &ct) { return ct.title == title; }); } -QString CMakeProject::displayName() const -{ - auto root = dynamic_cast<CMakeProjectNode *>(rootProjectNode()); - return root ? root->displayName() : projectDirectory().fileName(); -} - Project::RestoreResult CMakeProject::fromMap(const QVariantMap &map, QString *errorMessage) { RestoreResult result = Project::fromMap(map, errorMessage); diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h index f0bc68ce12..2b20a7479c 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.h +++ b/src/plugins/cmakeprojectmanager/cmakeproject.h @@ -86,8 +86,6 @@ public: explicit CMakeProject(const Utils::FileName &filename); ~CMakeProject() final; - QString displayName() const final; - QStringList buildTargetTitles(bool runnable = false) const; bool hasBuildTarget(const QString &title) const; diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp index 844a8a95bb..7464261e8c 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp @@ -57,7 +57,7 @@ CMakeManager::CMakeManager() : m_runCMakeAction(new QAction(QIcon(), tr("Run CMake"), this)), m_clearCMakeCacheAction(new QAction(QIcon(), tr("Clear CMake Configuration"), this)), m_runCMakeActionContextMenu(new QAction(QIcon(), tr("Run CMake"), this)), - m_rescanProjectAction(new QAction(QIcon(), tr("Rescan project"), this)) + m_rescanProjectAction(new QAction(QIcon(), tr("Rescan Project"), this)) { Core::ActionContainer *mbuild = Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT); diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp index 21b491e299..79337e0440 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.cpp @@ -56,12 +56,6 @@ bool CMakeInputsNode::showInSimpleTree() const return false; } -QList<ProjectExplorer::ProjectAction> CMakeInputsNode::supportedActions(ProjectExplorer::Node *node) const -{ - Q_UNUSED(node); - return QList<ProjectExplorer::ProjectAction>(); -} - CMakeListsNode::CMakeListsNode(const Utils::FileName &cmakeListPath) : ProjectExplorer::ProjectNode(cmakeListPath) { @@ -80,12 +74,6 @@ bool CMakeListsNode::showInSimpleTree() const return false; } -QList<ProjectExplorer::ProjectAction> CMakeListsNode::supportedActions(ProjectExplorer::Node *node) const -{ - Q_UNUSED(node); - return QList<ProjectExplorer::ProjectAction>(); -} - CMakeProjectNode::CMakeProjectNode(const Utils::FileName &directory) : ProjectExplorer::ProjectNode(directory) { @@ -103,12 +91,6 @@ QString CMakeProjectNode::tooltip() const return QString(); } -QList<ProjectExplorer::ProjectAction> CMakeProjectNode::supportedActions(ProjectExplorer::Node *node) const -{ - Q_UNUSED(node); - return QList<ProjectExplorer::ProjectAction>(); -} - CMakeTargetNode::CMakeTargetNode(const Utils::FileName &directory) : ProjectExplorer::ProjectNode(directory) { @@ -126,12 +108,6 @@ QString CMakeTargetNode::tooltip() const return m_tooltip; } -QList<ProjectExplorer::ProjectAction> CMakeTargetNode::supportedActions(ProjectExplorer::Node *node) const -{ - Q_UNUSED(node); - return QList<ProjectExplorer::ProjectAction>(); -} - void CMakeTargetNode::setTargetInformation(const QList<Utils::FileName> &artifacts, const QString &type) { diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h index ca1fedd6e8..d4160c7762 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h +++ b/src/plugins/cmakeprojectmanager/cmakeprojectnodes.h @@ -28,8 +28,6 @@ #include <projectexplorer/projectnodes.h> namespace CMakeProjectManager { -class CMakeProject; - namespace Internal { class CMakeInputsNode : public ProjectExplorer::ProjectNode @@ -40,7 +38,6 @@ public: static Utils::FileName inputsPathFromCMakeListsPath(const Utils::FileName &cmakeLists); bool showInSimpleTree() const final; - QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const final; }; class CMakeListsNode : public ProjectExplorer::ProjectNode @@ -49,7 +46,6 @@ public: CMakeListsNode(const Utils::FileName &cmakeListPath); bool showInSimpleTree() const final; - QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const final; }; class CMakeProjectNode : public ProjectExplorer::ProjectNode @@ -59,7 +55,6 @@ public: bool showInSimpleTree() const final; QString tooltip() const final; - QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const final; }; class CMakeTargetNode : public ProjectExplorer::ProjectNode @@ -71,7 +66,6 @@ public: bool showInSimpleTree() const final; QString tooltip() const final; - QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const final; private: QString m_tooltip; diff --git a/src/plugins/cmakeprojectmanager/servermodereader.cpp b/src/plugins/cmakeprojectmanager/servermodereader.cpp index 9225642025..a83ca14aaf 100644 --- a/src/plugins/cmakeprojectmanager/servermodereader.cpp +++ b/src/plugins/cmakeprojectmanager/servermodereader.cpp @@ -414,14 +414,16 @@ void ServerModeReader::extractConfigurationData(const QVariantMap &data) { const QString name = data.value(NAME_KEY).toString(); Q_UNUSED(name); + QSet<QString> knownTargets; // To filter duplicate target names:-/ const QVariantList projects = data.value("projects").toList(); for (const QVariant &p : projects) { const QVariantMap pData = p.toMap(); - m_projects.append(extractProjectData(pData)); + m_projects.append(extractProjectData(pData, knownTargets)); } } -ServerModeReader::Project *ServerModeReader::extractProjectData(const QVariantMap &data) +ServerModeReader::Project *ServerModeReader::extractProjectData(const QVariantMap &data, + QSet<QString> &knownTargets) { auto project = new Project; project->name = data.value(NAME_KEY).toString(); @@ -430,16 +432,28 @@ ServerModeReader::Project *ServerModeReader::extractProjectData(const QVariantMa const QVariantList targets = data.value("targets").toList(); for (const QVariant &t : targets) { const QVariantMap tData = t.toMap(); - project->targets.append(extractTargetData(tData, project)); + Target *tp = extractTargetData(tData, project, knownTargets); + if (tp) + project->targets.append(tp); } return project; } -ServerModeReader::Target *ServerModeReader::extractTargetData(const QVariantMap &data, Project *p) +ServerModeReader::Target *ServerModeReader::extractTargetData(const QVariantMap &data, Project *p, + QSet<QString> &knownTargets) { + const QString targetName = data.value(NAME_KEY).toString(); + + // Remove duplicate targets: CMake unfortunately does duplicate targets for all projects that + // contain them. Keep at least till cmake 3.9 is deprecated. + const int count = knownTargets.count(); + knownTargets.insert(targetName); + if (knownTargets.count() == count) + return nullptr; + auto target = new Target; target->project = p; - target->name = data.value(NAME_KEY).toString(); + target->name = targetName; target->sourceDirectory = FileName::fromString(data.value(SOURCE_DIRECTORY_KEY).toString()); target->buildDirectory = FileName::fromString(data.value("buildDirectory").toString()); @@ -664,7 +678,6 @@ void ServerModeReader::addHeaderNodes(ProjectNode *root, const QList<FileNode *> { auto headerNode = new VirtualFolderNode(root->filePath(), Node::DefaultPriority - 5); headerNode->setDisplayName(tr("<Headers>")); - root->addNode(headerNode); // knownHeaders are already listed in their targets: QSet<Utils::FileName> seenHeaders = Utils::transform<QSet>(knownHeaders, &FileNode::filePath); @@ -681,6 +694,11 @@ void ServerModeReader::addHeaderNodes(ProjectNode *root, const QList<FileNode *> headerNode->addNestedNode(node); } } + + if (headerNode->nodes().isEmpty()) + delete headerNode; // No Headers, do not show this Folder. + else + root->addNode(headerNode); } } // namespace Internal diff --git a/src/plugins/cmakeprojectmanager/servermodereader.h b/src/plugins/cmakeprojectmanager/servermodereader.h index 01c98182ea..3b1202c009 100644 --- a/src/plugins/cmakeprojectmanager/servermodereader.h +++ b/src/plugins/cmakeprojectmanager/servermodereader.h @@ -107,8 +107,8 @@ private: void extractCodeModelData(const QVariantMap &data); void extractConfigurationData(const QVariantMap &data); - Project *extractProjectData(const QVariantMap &data); - Target *extractTargetData(const QVariantMap &data, Project *p); + Project *extractProjectData(const QVariantMap &data, QSet<QString> &knownTargets); + Target *extractTargetData(const QVariantMap &data, Project *p, QSet<QString> &knownTargets); FileGroup *extractFileGroupData(const QVariantMap &data, const QDir &srcDir, Target *t); void extractCMakeInputsData(const QVariantMap &data); void extractCacheData(const QVariantMap &data); diff --git a/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp b/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp index b7b1d8c4b4..bad8aef38e 100644 --- a/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp +++ b/src/plugins/coreplugin/dialogs/saveitemsdialog.cpp @@ -60,7 +60,7 @@ SaveItemsDialog::SaveItemsDialog(QWidget *parent, connect(m_diffButton, &QAbstractButton::clicked, this, &SaveItemsDialog::collectFilesToDiff); } - QPushButton *discardButton = m_ui.buttonBox->addButton(tr("Do ¬ Save"), discardButtonRole); + QPushButton *discardButton = m_ui.buttonBox->addButton(tr("Do &Not Save"), discardButtonRole); m_ui.buttonBox->button(QDialogButtonBox::Save)->setDefault(true); m_ui.treeWidget->setFocus(); diff --git a/src/plugins/coreplugin/editormanager/documentmodel.cpp b/src/plugins/coreplugin/editormanager/documentmodel.cpp index 465ff09ae8..bf88039cc8 100644 --- a/src/plugins/coreplugin/editormanager/documentmodel.cpp +++ b/src/plugins/coreplugin/editormanager/documentmodel.cpp @@ -283,7 +283,7 @@ QVariant DocumentModelPrivate::data(const QModelIndex &index, int role) const case Qt::ToolTipRole: return entry->fileName().isEmpty() ? entry->displayName() : entry->fileName().toUserOutput(); default: - return QVariant(); + break; } return QVariant(); } diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index 44c010ba8f..1bfb5face4 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -931,6 +931,7 @@ Id EditorManagerPrivate::getOpenWithEditorId(const QString &fileName, bool *isEx // Built-in const EditorManager::EditorFactoryList editors = EditorManager::editorFactories(mt, false); const int size = editors.size(); + allEditorDisplayNames.reserve(size); for (int i = 0; i < size; i++) { allEditorIds.push_back(editors.at(i)->id()); allEditorDisplayNames.push_back(editors.at(i)->displayName()); diff --git a/src/plugins/coreplugin/editormanager/editorview.cpp b/src/plugins/coreplugin/editormanager/editorview.cpp index 99556068e1..b46c71f772 100644 --- a/src/plugins/coreplugin/editormanager/editorview.cpp +++ b/src/plugins/coreplugin/editormanager/editorview.cpp @@ -547,7 +547,7 @@ void EditorView::updateCurrentPositionInNavigationHistory() namespace { static inline bool fileNameWasRemoved(const QString &fileName) { - return !fileName.isEmpty() && !QFileInfo(fileName).exists(); + return !fileName.isEmpty() && !QFileInfo::exists(fileName); } } // End of anonymous namespace diff --git a/src/plugins/coreplugin/find/itemviewfind.cpp b/src/plugins/coreplugin/find/itemviewfind.cpp index 3588f125ec..cc2dd466e5 100644 --- a/src/plugins/coreplugin/find/itemviewfind.cpp +++ b/src/plugins/coreplugin/find/itemviewfind.cpp @@ -249,12 +249,13 @@ QModelIndex ItemViewFind::nextIndex(const QModelIndex &idx, bool *wrapped) const return model->index(0, 0); // same parent has more columns, go to next column - if (idx.column() + 1 < model->columnCount(idx.parent())) - return model->index(idx.row(), idx.column() + 1, idx.parent()); + const QModelIndex parentIdx = idx.parent(); + if (idx.column() + 1 < model->columnCount(parentIdx)) + return model->index(idx.row(), idx.column() + 1, parentIdx); // tree views have their children attached to first column // make sure we are at first column - QModelIndex current = model->index(idx.row(), 0, idx.parent()); + QModelIndex current = model->index(idx.row(), 0, parentIdx); // check for children if (d->m_option == FetchMoreWhileSearching && model->canFetchMore(current)) diff --git a/src/plugins/coreplugin/find/searchresultwidget.cpp b/src/plugins/coreplugin/find/searchresultwidget.cpp index 842abb9aa5..2aa443630d 100644 --- a/src/plugins/coreplugin/find/searchresultwidget.cpp +++ b/src/plugins/coreplugin/find/searchresultwidget.cpp @@ -166,7 +166,7 @@ SearchResultWidget::SearchResultWidget(QWidget *parent) : connect(m_cancelButton, &QAbstractButton::clicked, this, &SearchResultWidget::cancel); m_searchAgainButton = new QToolButton(topFindWidget); m_searchAgainButton->setToolTip(tr("Repeat the search with same parameters.")); - m_searchAgainButton->setText(tr("&Search again")); + m_searchAgainButton->setText(tr("&Search Again")); m_searchAgainButton->setToolButtonStyle(Qt::ToolButtonTextOnly); m_searchAgainButton->setVisible(false); connect(m_searchAgainButton, &QAbstractButton::clicked, this, &SearchResultWidget::searchAgain); diff --git a/src/plugins/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp index 2790075638..a9c9c59cad 100644 --- a/src/plugins/coreplugin/icore.cpp +++ b/src/plugins/coreplugin/icore.cpp @@ -448,12 +448,10 @@ static QString compilerString() #elif defined(Q_CC_MSVC) if (_MSC_VER > 1999) return QLatin1String("MSVC <unknown>"); - if (_MSC_VER >= 1900) // 1900: MSVC 2015 + if (_MSC_VER >= 1910) + return QLatin1String("MSVC 2017"); + if (_MSC_VER >= 1900) return QLatin1String("MSVC 2015"); - if (_MSC_VER >= 1800) // 1800: MSVC 2013 (yearly release cycle) - return QLatin1String("MSVC ") + QString::number(2008 + ((_MSC_VER / 100) - 13)); - if (_MSC_VER >= 1500) // 1500: MSVC 2008, 1600: MSVC 2010, ... (2-year release cycle) - return QLatin1String("MSVC ") + QString::number(2008 + 2 * ((_MSC_VER / 100) - 15)); #endif return QLatin1String("<unknown compiler>"); } diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp index 71e1f9a5a9..f587f0cc36 100644 --- a/src/plugins/coreplugin/mainwindow.cpp +++ b/src/plugins/coreplugin/mainwindow.cpp @@ -710,7 +710,7 @@ void MainWindow::registerDefaultActions() m_toggleRightSideBarAction->setCheckable(true); cmd = ActionManager::registerAction(m_toggleRightSideBarAction, Constants::TOGGLE_RIGHT_SIDEBAR); cmd->setAttribute(Command::CA_UpdateText); - cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Ctrl+Meta+0") : tr("Ctrl+Shift+0"))); + cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("Ctrl+Meta+0") : tr("Ctrl+0"))); connect(m_toggleRightSideBarAction, &QAction::triggered, this, [this](bool visible) { setSidebarVisible(visible, Side::Right); }); ProxyAction *toggleRightSideBarProxyAction = diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp index 375d38afb5..4a68fa4110 100644 --- a/src/plugins/coreplugin/manhattanstyle.cpp +++ b/src/plugins/coreplugin/manhattanstyle.cpp @@ -231,12 +231,12 @@ QPalette ManhattanStyle::standardPalette() const void ManhattanStyle::polish(QApplication *app) { - return QProxyStyle::polish(app); + QProxyStyle::polish(app); } void ManhattanStyle::unpolish(QApplication *app) { - return QProxyStyle::unpolish(app); + QProxyStyle::unpolish(app); } QPalette panelPalette(const QPalette &oldPalette, bool lightColored = false) @@ -381,8 +381,10 @@ int ManhattanStyle::styleHint(StyleHint hint, const QStyleOption *option, const void ManhattanStyle::drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { - if (!panelWidget(widget)) - return QProxyStyle::drawPrimitive(element, option, painter, widget); + if (!panelWidget(widget)) { + QProxyStyle::drawPrimitive(element, option, painter, widget); + return; + } bool animating = (option->state & State_Animating); int state = option->state; @@ -614,8 +616,10 @@ void ManhattanStyle::drawPrimitive(PrimitiveElement element, const QStyleOption void ManhattanStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const { - if (!panelWidget(widget) && !qobject_cast<const QMenu *>(widget)) - return QProxyStyle::drawControl(element, option, painter, widget); + if (!panelWidget(widget) && !qobject_cast<const QMenu *>(widget)) { + QProxyStyle::drawControl(element, option, painter, widget); + return; + } switch (element) { case CE_MenuItem: diff --git a/src/plugins/coreplugin/plugindialog.cpp b/src/plugins/coreplugin/plugindialog.cpp index 27436952fb..f55ddd5e2a 100644 --- a/src/plugins/coreplugin/plugindialog.cpp +++ b/src/plugins/coreplugin/plugindialog.cpp @@ -61,7 +61,7 @@ PluginDialog::PluginDialog(QWidget *parent) m_view, &ExtensionSystem::PluginView::setFilter); filterLayout->addWidget(filterEdit); m_view->setShowHidden(false); - auto showHidden = new QCheckBox(tr("Show All")); + auto showHidden = new QCheckBox(tr("Show all")); showHidden->setToolTip(tr("Show all installed plugins, including base plugins " "and plugins that are not available on this platform.")); showHidden->setChecked(m_view->isShowingHidden()); diff --git a/src/plugins/coreplugin/statusbarmanager.cpp b/src/plugins/coreplugin/statusbarmanager.cpp index 33eaf62d71..cae5177f19 100644 --- a/src/plugins/coreplugin/statusbarmanager.cpp +++ b/src/plugins/coreplugin/statusbarmanager.cpp @@ -157,5 +157,5 @@ void NonResizingSplitter::resizeEvent(QResizeEvent *ev) int leftSplitWidth = qMin(sizes().at(0), ev->size().width()); int rightSplitWidth = qMax(0, ev->size().width() - leftSplitWidth); setSizes(QList<int>() << leftSplitWidth << rightSplitWidth); - return QWidget::resizeEvent(ev); + QWidget::resizeEvent(ev); } diff --git a/src/plugins/coreplugin/themechooser.cpp b/src/plugins/coreplugin/themechooser.cpp index 6f81aaa950..ff62406dba 100644 --- a/src/plugins/coreplugin/themechooser.cpp +++ b/src/plugins/coreplugin/themechooser.cpp @@ -221,8 +221,16 @@ QList<ThemeEntry> ThemeEntry::availableThemes() Id ThemeEntry::themeSetting() { - return Id::fromSetting(ICore::settings()->value(QLatin1String(Constants::SETTINGS_THEME), - QLatin1String(Constants::DEFAULT_THEME))); + const Id setting = + Id::fromSetting(ICore::settings()->value(QLatin1String(Constants::SETTINGS_THEME), + QLatin1String(Constants::DEFAULT_THEME))); + + const QList<ThemeEntry> themes = availableThemes(); + if (themes.empty()) + return Id(); + const bool settingValid = Utils::contains(themes, Utils::equal(&ThemeEntry::id, setting)); + + return settingValid ? setting : themes.first().id(); } Theme *ThemeEntry::createTheme(Id id) diff --git a/src/plugins/coreplugin/variablechooser.cpp b/src/plugins/coreplugin/variablechooser.cpp index 4d336e61c7..dc595b61d9 100644 --- a/src/plugins/coreplugin/variablechooser.cpp +++ b/src/plugins/coreplugin/variablechooser.cpp @@ -499,7 +499,7 @@ void VariableChooserPrivate::updateCurrentEditor(QWidget *old, QWidget *widget) m_textEdit = 0; m_plainTextEdit = 0; QWidget *chooser = widget->property(kVariableSupportProperty).value<QWidget *>(); - m_currentVariableName = widget->property(kVariableNameProperty).value<QByteArray>(); + m_currentVariableName = widget->property(kVariableNameProperty).toByteArray(); bool supportsVariables = chooser == q; if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget)) m_lineEdit = (supportsVariables ? lineEdit : 0); diff --git a/src/plugins/cpaster/authenticationdialog.cpp b/src/plugins/cpaster/authenticationdialog.cpp new file mode 100644 index 0000000000..326cdb2d80 --- /dev/null +++ b/src/plugins/cpaster/authenticationdialog.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "authenticationdialog.h" + +#include <QDialogButtonBox> +#include <QFormLayout> +#include <QLabel> +#include <QLineEdit> +#include <QVBoxLayout> + +namespace CodePaster { + +AuthenticationDialog::AuthenticationDialog(const QString &details, QWidget *parent) + : QDialog(parent) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + auto *mainLayout = new QVBoxLayout; + mainLayout->addWidget(new QLabel(details)); + auto *formLayout = new QFormLayout; + formLayout->addRow(tr("Username:"), m_user = new QLineEdit); + formLayout->addRow(tr("Password:"), m_pass = new QLineEdit); + m_pass->setEchoMode(QLineEdit::Password); + mainLayout->addLayout(formLayout); + auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + mainLayout->addWidget(buttonBox); + setLayout(mainLayout); +} + +QString AuthenticationDialog::userName() const +{ + return m_user->text(); +} + +QString AuthenticationDialog::password() const +{ + return m_pass->text(); +} + +} // namespace CodePaster diff --git a/src/plugins/cpaster/authenticationdialog.h b/src/plugins/cpaster/authenticationdialog.h new file mode 100644 index 0000000000..f52dbaef24 --- /dev/null +++ b/src/plugins/cpaster/authenticationdialog.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QDialog> + +QT_BEGIN_NAMESPACE +class QLineEdit; +QT_END_NAMESPACE + +namespace CodePaster { + +class AuthenticationDialog : public QDialog +{ +public: + AuthenticationDialog(const QString &details, QWidget *parent = nullptr); + + bool authenticated() const { return m_authenticated; } + QString userName() const; + QString password() const; + +private: + bool m_authenticated = false; + QLineEdit *m_user = nullptr; + QLineEdit *m_pass = nullptr; +}; + +} // namespace CodePaster diff --git a/src/plugins/cpaster/cpaster.pro b/src/plugins/cpaster/cpaster.pro index cc30094162..20c85c596b 100644 --- a/src/plugins/cpaster/cpaster.pro +++ b/src/plugins/cpaster/cpaster.pro @@ -14,7 +14,8 @@ HEADERS += cpasterplugin.h \ fileshareprotocolsettingspage.h \ kdepasteprotocol.h \ urlopenprotocol.h \ - codepasterservice.h + codepasterservice.h \ + authenticationdialog.h SOURCES += cpasterplugin.cpp \ settingspage.cpp \ @@ -28,7 +29,8 @@ SOURCES += cpasterplugin.cpp \ fileshareprotocol.cpp \ fileshareprotocolsettingspage.cpp \ kdepasteprotocol.cpp \ - urlopenprotocol.cpp + urlopenprotocol.cpp \ + authenticationdialog.cpp FORMS += settingspage.ui \ pasteselect.ui \ @@ -39,3 +41,5 @@ include(../../shared/cpaster/cpaster.pri) RESOURCES += \ cpaster.qrc + +DEFINES *= CPASTER_PLUGIN_GUI diff --git a/src/plugins/cpaster/cpaster.qbs b/src/plugins/cpaster/cpaster.qbs index 4b3cb99a84..f9144b53d8 100644 --- a/src/plugins/cpaster/cpaster.qbs +++ b/src/plugins/cpaster/cpaster.qbs @@ -45,6 +45,8 @@ QtcPlugin { "settingspage.ui", "urlopenprotocol.cpp", "urlopenprotocol.h", + "authenticationdialog.cpp", + "authenticationdialog.h" ] Group { diff --git a/src/plugins/cpaster/kdepasteprotocol.cpp b/src/plugins/cpaster/kdepasteprotocol.cpp index d182005c1d..c5c09cb9bf 100644 --- a/src/plugins/cpaster/kdepasteprotocol.cpp +++ b/src/plugins/cpaster/kdepasteprotocol.cpp @@ -24,7 +24,11 @@ ****************************************************************************/ #include "kdepasteprotocol.h" +#ifdef CPASTER_PLUGIN_GUI +#include "authenticationdialog.h" +#endif +#include <coreplugin/icore.h> #include <utils/qtcassert.h> #include <QDebug> @@ -33,7 +37,7 @@ #include <QJsonDocument> #include <QJsonObject> #include <QJsonArray> - +#include <QRegularExpression> #include <QNetworkReply> #include <algorithm> @@ -118,7 +122,7 @@ void StickyNotesPasteProtocol::paste(const QString &text, pasteData += QUrl::toPercentEncoding(description.left(maxDescriptionLength)); } - m_pasteReply = httpPost(m_hostUrl + QLatin1String("api/json/create"), pasteData); + m_pasteReply = httpPost(m_hostUrl + QLatin1String("api/json/create"), pasteData, true); connect(m_pasteReply, &QNetworkReply::finished, this, &StickyNotesPasteProtocol::pasteFinished); if (debug) qDebug() << "paste: sending " << m_pasteReply << pasteData; @@ -262,9 +266,127 @@ void StickyNotesPasteProtocol::listFinished() m_listReply = nullptr; } +KdePasteProtocol::KdePasteProtocol() +{ + setHostUrl(QLatin1String("https://pastebin.kde.org/")); + connect(this, &KdePasteProtocol::authenticationFailed, this, [this] () { + m_loginFailed = true; + paste(m_text, m_contentType, m_expiryDays, QString(), QString(), m_description); + }); +} + +void KdePasteProtocol::paste(const QString &text, Protocol::ContentType ct, int expiryDays, + const QString &username, const QString &comment, + const QString &description) +{ + Q_UNUSED(username); + Q_UNUSED(comment); + // KDE paster needs authentication nowadays +#ifdef CPASTER_PLUGIN_GUI + QString details = tr("Pasting to KDE paster needs authentication.<br/>" + "Enter your KDE Identity credentials to continue."); + if (m_loginFailed) + details.prepend(tr("<span style='background-color:LightYellow;color:red'>Login failed</span><br/><br/>")); + + AuthenticationDialog authDialog(details, Core::ICore::dialogParent()); + authDialog.setWindowTitle("Authenticate for KDE paster"); + if (authDialog.exec() != QDialog::Accepted) { + m_loginFailed = false; + return; + } + const QString user = authDialog.userName(); + const QString passwd = authDialog.password(); +#else + // FIXME get the credentials for the cmdline cpaster somehow + const QString user; + const QString passwd; + qDebug() << "KDE needs credentials for pasting"; + return; +#endif + // store input data as members to be able to use them after the authentication succeeded + m_text = text; + m_contentType = ct; + m_expiryDays = expiryDays; + m_description = description; + authenticate(user, passwd); +} + QString KdePasteProtocol::protocolName() { return QLatin1String("Paste.KDE.Org"); } +void KdePasteProtocol::authenticate(const QString &user, const QString &passwd) +{ + QTC_ASSERT(!m_authReply, return); + + // first we need to obtain the hidden form token for logging in + m_authReply = httpGet(hostUrl() + "user/login"); + connect(m_authReply, &QNetworkReply::finished, this, [this, user, passwd] () { + onPreAuthFinished(user, passwd); + }); +} + +void KdePasteProtocol::onPreAuthFinished(const QString &user, const QString &passwd) +{ + if (m_authReply->error() != QNetworkReply::NoError) { + m_authReply->deleteLater(); + m_authReply = nullptr; + return; + } + const QByteArray page = m_authReply->readAll(); + m_authReply->deleteLater(); + const QRegularExpression regex("name=\"_token\"\\s+type=\"hidden\"\\s+value=\"(.*?)\">"); + const QRegularExpressionMatch match = regex.match(QLatin1String(page)); + if (!match.hasMatch()) { + m_authReply = nullptr; + return; + } + const QString token = match.captured(1); + + QByteArray data("username=" + QUrl::toPercentEncoding(user) + + "&password=" + QUrl::toPercentEncoding(passwd) + + "&_token=" + QUrl::toPercentEncoding(token)); + m_authReply = httpPost(hostUrl() + "user/login", data, true); + connect(m_authReply, &QNetworkReply::finished, this, &KdePasteProtocol::onAuthFinished); +} + +void KdePasteProtocol::onAuthFinished() +{ + if (m_authReply->error() != QNetworkReply::NoError) { + m_authReply->deleteLater(); + m_authReply = nullptr; + return; + } + const QVariant attribute = m_authReply->attribute(QNetworkRequest::RedirectionTargetAttribute); + m_redirectUrl = redirectUrl(attribute.toUrl().toString(), m_redirectUrl); + if (!m_redirectUrl.isEmpty()) { // we need to perform a redirect + QUrl url(m_redirectUrl); + if (url.path().isEmpty()) + url.setPath("/"); // avoid issue inside cookiesForUrl() + m_authReply->deleteLater(); + m_authReply = httpGet(url.url(), true); + connect(m_authReply, &QNetworkReply::finished, this, &KdePasteProtocol::onAuthFinished); + } else { // auth should be done now + const QByteArray page = m_authReply->readAll(); + m_authReply->deleteLater(); + m_authReply = nullptr; + if (page.contains("https://identity.kde.org")) // we're back on the login page + emit authenticationFailed(); + else { + m_loginFailed = false; + StickyNotesPasteProtocol::paste(m_text, m_contentType, m_expiryDays, QString(), + QString(), m_description); + } + } +} + +QString KdePasteProtocol::redirectUrl(const QString &redirect, const QString &oldRedirect) const +{ + QString redirectUrl; + if (!redirect.isEmpty() && redirect != oldRedirect) + redirectUrl = redirect; + return redirectUrl; +} + } // namespace CodePaster diff --git a/src/plugins/cpaster/kdepasteprotocol.h b/src/plugins/cpaster/kdepasteprotocol.h index 7cd3d0d83b..e7bbd1ba1d 100644 --- a/src/plugins/cpaster/kdepasteprotocol.h +++ b/src/plugins/cpaster/kdepasteprotocol.h @@ -70,14 +70,33 @@ private: class KdePasteProtocol : public StickyNotesPasteProtocol { + Q_OBJECT public: - KdePasteProtocol() - { - setHostUrl(QLatin1String("https://pastebin.kde.org/")); - } + KdePasteProtocol(); + + void paste(const QString &text, ContentType ct = Text, int expiryDays = 1, + const QString &username = QString(), + const QString &comment = QString() , + const QString &description = QString()) override; QString name() const override { return protocolName(); } static QString protocolName(); +signals: + void authenticationFailed(); +private: + void authenticate(const QString &user, const QString &passwd); + void onPreAuthFinished(const QString &user, const QString &passwd); + void onAuthFinished(); + QString redirectUrl(const QString &redirect, const QString &oldRedirect) const; + + QNetworkReply *m_authReply = nullptr; + QString m_text; + ContentType m_contentType = Text; + int m_expiryDays = 1; + bool m_loginFailed = false; + QString m_description; + QString m_redirectUrl; + }; } // namespace CodePaster diff --git a/src/plugins/cpaster/pastebindotcaprotocol.cpp b/src/plugins/cpaster/pastebindotcaprotocol.cpp index 6d413a37b7..aeb83d20d4 100644 --- a/src/plugins/cpaster/pastebindotcaprotocol.cpp +++ b/src/plugins/cpaster/pastebindotcaprotocol.cpp @@ -28,9 +28,11 @@ #include <utils/qtcassert.h> #include <QNetworkReply> -#include <QXmlStreamReader> -#include <QXmlStreamAttributes> #include <QStringList> +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonValue> +#include <QJsonObject> static const char urlC[] = "http://pastebin.ca/"; static const char internalUrlC[] = "http://pbin.ca/"; @@ -180,58 +182,39 @@ bool PasteBinDotCaProtocol::checkConfiguration(QString *errorMessage) return ok; } -/* Quick & dirty: Parse the <div>-elements with the "Recent Posts" listing - * out of the page. +/* Quick & dirty: Parse page does no more work due to internal javascript/websocket magic - so, + * search for _initial_ json array containing the last added pastes. \code -<div class="menutitle"><h2>Recent Posts</h2></div> - <div class="items" id="idmenurecent-collapse"> - <div class='recentlink'> - <a href="/[id]" class="rjt" rel="/preview.php?id=[id]">[nameTitle]</a> - <div class='recentdetail'>[time spec]</div> - </div> - ...<h2>Create a New Pastebin Post</h2> +<script type="text/javascript">var pHistoryInitial = [{"id":3791300,"ts":1491288268,"name":"try", +"expires":1491374668}, \endcode */ static inline QStringList parseLists(QIODevice *io) { - enum State { OutsideRecentLink, InsideRecentLink }; - QStringList rc; - const QString classAttribute = QLatin1String("class"); - const QString divElement = QLatin1String("div"); - const QString anchorElement = QLatin1String("a"); - - // Start parsing at the 'recent posts' entry as the HTML above is not well-formed - // as of 8.4.2010. This will then terminate with an error. QByteArray data = io->readAll(); - const QByteArray recentPosts("<h2>Recent Posts</h2></div>"); - const int recentPostsPos = data.indexOf(recentPosts); - if (recentPostsPos == -1) + const QByteArray history("<script type=\"text/javascript\">var pHistoryInitial = "); + int pos = data.indexOf(history); + if (pos == -1) + return rc; + data.remove(0, pos + history.size()); + pos = data.indexOf(";</script>"); + if (pos == -1) return rc; - data.remove(0, recentPostsPos + recentPosts.size()); - QXmlStreamReader reader(data); - State state = OutsideRecentLink; - while (!reader.atEnd()) { - switch (reader.readNext()) { - case QXmlStreamReader::StartElement: - // Inside a <div> of an entry: Anchor or description - if (state == InsideRecentLink && reader.name() == anchorElement) { // Anchor - // Strip host from link - QString link = reader.attributes().value(QLatin1String("href")).toString(); - if (link.startsWith(QLatin1Char('/'))) - link.remove(0, 1); - const QString nameTitle = reader.readElementText(); - rc.push_back(link + QLatin1Char(' ') + nameTitle); - } else if (state == OutsideRecentLink && reader.name() == divElement) { // "<div>" state switching - if (reader.attributes().value(classAttribute) == QLatin1String("recentlink")) - state = InsideRecentLink; - } // divElement - break; - default: - break; - } // switch reader - } // while reader.atEnd() + data.truncate(pos); + QJsonParseError error; + const QJsonDocument doc = QJsonDocument::fromJson(data, &error); + if (error.error != QJsonParseError::NoError) + return rc; + QJsonArray array = doc.array(); + for (const QJsonValue &val : array) { + const QJsonObject obj = val.toObject(); + const QJsonValue id = obj.value("id"); + const QJsonValue name = obj.value("name"); + if (!id.isUndefined()) + rc.append(QString::number(id.toInt()) + ' ' + name.toString()); + } return rc; } diff --git a/src/plugins/cpaster/pastebindotcomprotocol.cpp b/src/plugins/cpaster/pastebindotcomprotocol.cpp index 20e071d537..cdd3d18c05 100644 --- a/src/plugins/cpaster/pastebindotcomprotocol.cpp +++ b/src/plugins/cpaster/pastebindotcomprotocol.cpp @@ -37,7 +37,7 @@ enum { debug = 0 }; -static const char PASTEBIN_BASE[]="http://pastebin.com/"; +static const char PASTEBIN_BASE[]="https://pastebin.com/"; static const char PASTEBIN_API[]="api/api_post.php"; static const char PASTEBIN_RAW[]="raw/"; static const char PASTEBIN_ARCHIVE[]="archive"; diff --git a/src/plugins/cpaster/protocol.cpp b/src/plugins/cpaster/protocol.cpp index f79bed2a46..8b745006d2 100644 --- a/src/plugins/cpaster/protocol.cpp +++ b/src/plugins/cpaster/protocol.cpp @@ -35,6 +35,8 @@ #include <coreplugin/icore.h> #include <coreplugin/dialogs/ioptionspage.h> +#include <QNetworkCookie> +#include <QNetworkCookieJar> #include <QNetworkRequest> #include <QNetworkReply> @@ -173,17 +175,30 @@ bool Protocol::showConfigurationError(const Protocol *p, // --------- NetworkProtocol -QNetworkReply *NetworkProtocol::httpGet(const QString &link) +static void addCookies(QNetworkRequest &request) +{ + auto accessMgr = Utils::NetworkAccessManager::instance(); + const QList<QNetworkCookie> cookies = accessMgr->cookieJar()->cookiesForUrl(request.url()); + for (const QNetworkCookie &cookie : cookies) + request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookie)); +} + +QNetworkReply *NetworkProtocol::httpGet(const QString &link, bool handleCookies) { QUrl url(link); QNetworkRequest r(url); + if (handleCookies) + addCookies(r); return Utils::NetworkAccessManager::instance()->get(r); } -QNetworkReply *NetworkProtocol::httpPost(const QString &link, const QByteArray &data) +QNetworkReply *NetworkProtocol::httpPost(const QString &link, const QByteArray &data, + bool handleCookies) { QUrl url(link); QNetworkRequest r(url); + if (handleCookies) + addCookies(r); r.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(QByteArray("application/x-www-form-urlencoded"))); return Utils::NetworkAccessManager::instance()->post(r, data); diff --git a/src/plugins/cpaster/protocol.h b/src/plugins/cpaster/protocol.h index 76891ae279..4dc2acb6b2 100644 --- a/src/plugins/cpaster/protocol.h +++ b/src/plugins/cpaster/protocol.h @@ -110,9 +110,10 @@ public: ~NetworkProtocol() override; protected: - QNetworkReply *httpGet(const QString &url); + QNetworkReply *httpGet(const QString &url, bool handleCookies = false); - QNetworkReply *httpPost(const QString &link, const QByteArray &data); + QNetworkReply *httpPost(const QString &link, const QByteArray &data, + bool handleCookies = false); // Check connectivity of host, displaying a message box. bool httpStatus(QString url, QString *errorMessage, bool useHttps = false); diff --git a/src/plugins/cppeditor/cppautocompleter.cpp b/src/plugins/cppeditor/cppautocompleter.cpp index d980e53e37..d5a97c38ae 100644 --- a/src/plugins/cppeditor/cppautocompleter.cpp +++ b/src/plugins/cppeditor/cppautocompleter.cpp @@ -131,7 +131,7 @@ static QString fileContent(int fileContent, QChar charToInsert) case ']': return QLatin1String("[[|]"); default: return QString(); } - default: return QString(); + default: break; } return QString(); } diff --git a/src/plugins/cpptools/modelmanagertesthelper.cpp b/src/plugins/cpptools/modelmanagertesthelper.cpp index 9eebbee15d..c21760f1b2 100644 --- a/src/plugins/cpptools/modelmanagertesthelper.cpp +++ b/src/plugins/cpptools/modelmanagertesthelper.cpp @@ -42,13 +42,10 @@ TestProject::TestProject(const QString &name, QObject *parent) : { setParent(parent); setId(Core::Id::fromString(name)); + setDisplayName(name); qRegisterMetaType<QSet<QString> >(); } -TestProject::~TestProject() -{ -} - ModelManagerTestHelper::ModelManagerTestHelper(QObject *parent, bool testOnlyForCleanedProjects) : QObject(parent) diff --git a/src/plugins/cpptools/modelmanagertesthelper.h b/src/plugins/cpptools/modelmanagertesthelper.h index 18f0d6b5ef..47e49fefe9 100644 --- a/src/plugins/cpptools/modelmanagertesthelper.h +++ b/src/plugins/cpptools/modelmanagertesthelper.h @@ -41,9 +41,6 @@ class CPPTOOLS_EXPORT TestProject: public ProjectExplorer::Project public: TestProject(const QString &name, QObject *parent); - ~TestProject() override; - - QString displayName() const override { return m_name; } private: QString m_name; diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp index 8c104e0dd8..eba7a8025c 100644 --- a/src/plugins/debugger/breakhandler.cpp +++ b/src/plugins/debugger/breakhandler.cpp @@ -363,19 +363,19 @@ BreakpointDialog::BreakpointDialog(Breakpoint b, QWidget *parent) // Match BreakpointType (omitting unknown type). const QStringList types = { - tr("File name and line number"), - tr("Function name"), - tr("Break on memory address"), - tr("Break when C++ exception is thrown"), - tr("Break when C++ exception is caught"), - tr("Break when function \"main\" starts"), - tr("Break when a new process is forked"), - tr("Break when a new process is executed"), - tr("Break when a system call is executed"), - tr("Break on data access at fixed address"), - tr("Break on data access at address given by expression"), - tr("Break on QML signal emit"), - tr("Break when JavaScript exception is thrown") + tr("File Name and Line Number"), + tr("Function Name"), + tr("Break on Memory Address"), + tr("Break When C++ Exception Is Thrown"), + tr("Break When C++ Exception Is Caught"), + tr("Break When Function \"main\" Starts"), + tr("Break When a New Process Is Forked"), + tr("Break When a New Process Is Executed"), + tr("Break When a System Call Is Executed"), + tr("Break on Data Access at Fixed Address"), + tr("Break on Data Access at Address Given by Expression"), + tr("Break on QML Signal Emit"), + tr("Break When JavaScript Exception Is Thrown") }; // We don't list UnknownBreakpointType, so 1 less: QTC_CHECK(types.size() + 1 == LastBreakpointType); diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 3531e882a6..af3939d445 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -1238,10 +1238,18 @@ void CdbEngine::doUpdateLocals(const UpdateParameters &updateParameters) cmd.arg("stringcutoff", action(MaximalStringLength)->value().toString()); cmd.arg("displaystringlimit", action(DisplayStringLimit)->value().toString()); + if (boolSetting(UseCodeModel)) { + QStringList uninitializedVariables; + getUninitializedVariables(Internal::cppCodeModelSnapshot(), + frame.function, frame.file, frame.line, &uninitializedVariables); + cmd.arg("uninitialized", uninitializedVariables); + } + cmd.callback = [this](const DebuggerResponse &response) { if (response.resultClass == ResultDone) { - showMessage(response.data.toString(), LogMisc); - updateLocalsView(response.data); + const GdbMi &result = response.data["result"]; + showMessage(result.toString(), LogMisc); + updateLocalsView(result); } else { showMessage(response.data["msg"].data(), LogError); } @@ -1428,6 +1436,21 @@ void CdbEngine::postResolveSymbol(const QString &module, const QString &function } } +void CdbEngine::showScriptMessages(const QString &message) const +{ + GdbMi gdmiMessage; + gdmiMessage.fromString(message); + if (!gdmiMessage.isValid()) + showMessage(message, LogMisc); + const GdbMi &messages = gdmiMessage["msg"]; + for (const GdbMi &msg : messages.children()) { + if (msg.name() == "bridgemessage") + showMessage(msg["msg"].data(), LogMisc); + else + showMessage(msg.data(), LogMisc); + } +} + // Parse address from 'x' response. // "00000001`3f7ebe80 module!foo (void)" static inline quint64 resolvedAddress(const QString &line) @@ -2228,7 +2251,7 @@ void CdbEngine::handleExtensionMessage(char t, int token, const QString &what, c // Is there a reply expected, some command queued? if (t == 'R' || t == 'N') { if (token == -1) { // Default token, user typed in extension command - showMessage(message, LogMisc); + showScriptMessages(message); return; } // Did the command finish? Take off queue and complete, invoke CB @@ -2239,7 +2262,7 @@ void CdbEngine::handleExtensionMessage(char t, int token, const QString &what, c if (!command.callback) { if (!message.isEmpty()) // log unhandled output - showMessage(message, LogMisc); + showScriptMessages(message); return; } DebuggerResponse response; @@ -2250,6 +2273,8 @@ void CdbEngine::handleExtensionMessage(char t, int token, const QString &what, c if (!response.data.isValid()) { response.data.m_data = message; response.data.m_type = GdbMi::Tuple; + } else { + showScriptMessages(message); } } else { response.resultClass = ResultError; @@ -2899,14 +2924,19 @@ void CdbEngine::handleAdditionalQmlStack(const DebuggerResponse &response) void CdbEngine::setupScripting(const DebuggerResponse &response) { - GdbMi data = response.data; + GdbMi data = response.data["msg"]; if (response.resultClass != ResultDone) { showMessage(data["msg"].data(), LogMisc); return; } - const QString &verOutput = data.data(); + if (data.childCount() == 0) { + showMessage(QString("No output from sys.version"), LogWarning); + return; + } + + const QString &verOutput = data.childAt(0).data(); const QString firstToken = verOutput.split(QLatin1Char(' ')).constFirst(); - const QVector<QStringRef> pythonVersion =firstToken.splitRef(QLatin1Char('.')); + const QVector<QStringRef> pythonVersion = firstToken.splitRef(QLatin1Char('.')); bool ok = false; if (pythonVersion.size() == 3) { @@ -2934,7 +2964,7 @@ void CdbEngine::setupScripting(const DebuggerResponse &response) runCommand({"theDumper = Dumper()", ScriptCommand}); runCommand({"theDumper.loadDumpers(None)", ScriptCommand, [this](const DebuggerResponse &response) { - watchHandler()->addDumpers(response.data["dumpers"]); + watchHandler()->addDumpers(response.data["result"]["dumpers"]); }}); } diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index aaf8b25319..ac2e0679d6 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -189,6 +189,7 @@ private: DisassemblerAgent *agent); void postResolveSymbol(const QString &module, const QString &function, DisassemblerAgent *agent); + void showScriptMessages(const QString &message) const; // Builtin commands void handleStackTrace(const DebuggerResponse &); void handleRegisters(const DebuggerResponse &); diff --git a/src/plugins/debugger/commonoptionspage.cpp b/src/plugins/debugger/commonoptionspage.cpp index 1972fe57cd..644fdabbda 100644 --- a/src/plugins/debugger/commonoptionspage.cpp +++ b/src/plugins/debugger/commonoptionspage.cpp @@ -115,13 +115,11 @@ QWidget *CommonOptionsPage::widget() "will automatically open views associated with the current location.") + QLatin1Char('\n'); auto checkBoxCloseSourceBuffersOnExit = new QCheckBox(behaviorBox); checkBoxCloseSourceBuffersOnExit->setText(tr("Close temporary source views on debugger exit")); - checkBoxCloseSourceBuffersOnExit->setToolTip(t + tr("Select this option to close " - "automatically opened source views when the debugger exits.")); + checkBoxCloseSourceBuffersOnExit->setToolTip(t + tr("Closes automatically opened source views when the debugger exits.")); auto checkBoxCloseMemoryBuffersOnExit = new QCheckBox(behaviorBox); checkBoxCloseMemoryBuffersOnExit->setText(tr("Close temporary memory views on debugger exit")); - checkBoxCloseMemoryBuffersOnExit->setToolTip(t + tr("Select this option to close " - "automatically opened memory views when the debugger exits.")); + checkBoxCloseMemoryBuffersOnExit->setToolTip(t + tr("Closes automatically opened memory views when the debugger exits.")); auto checkBoxSwitchModeOnExit = new QCheckBox(behaviorBox); checkBoxSwitchModeOnExit->setText(tr("Switch to previous mode on debugger exit")); diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 0d1bd2668b..58f2ae0054 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -2112,7 +2112,7 @@ void DebuggerEngine::checkState(DebuggerState state, const char *file, int line) return; QString msg = QString("UNEXPECTED STATE: %1 WANTED: %2 IN %3:%4") - .arg(current).arg(state).arg(QLatin1String(file)).arg(line); + .arg(stateName(current)).arg(stateName(state)).arg(QLatin1String(file)).arg(line); showMessage(msg, LogError); qDebug("%s", qPrintable(msg)); diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index c983c096ba..599ba02bf5 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -1913,12 +1913,13 @@ void DebuggerPluginPrivate::onCurrentProjectChanged(Project *project) for (int i = 0, n = m_snapshotHandler->size(); i != n; ++i) { // Run controls might be deleted during exit. if (DebuggerEngine *engine = m_snapshotHandler->at(i)) { - DebuggerRunControl *runControl = engine->runControl(); - RunConfiguration *rc = runControl->runConfiguration(); - if (rc == activeRc) { - m_snapshotHandler->setCurrentIndex(i); - updateState(engine); - return; + if (DebuggerRunControl *runControl = engine->runControl()) { + RunConfiguration *rc = runControl->runConfiguration(); + if (rc == activeRc) { + m_snapshotHandler->setCurrentIndex(i); + updateState(engine); + return; + } } } } diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp index 23a71d06e4..87e5e47dfa 100644 --- a/src/plugins/debugger/debuggerruncontrol.cpp +++ b/src/plugins/debugger/debuggerruncontrol.cpp @@ -648,7 +648,6 @@ class DummyProject : public Project { public: DummyProject() : Project(QString(""), FileName::fromString("")) {} - QString displayName() const final { return QString(); } }; RunConfiguration *dummyRunConfigForKit(ProjectExplorer::Kit *kit) diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 6b0740baca..db71ce6ecc 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -147,6 +147,11 @@ static bool isMostlyHarmlessMessage(const QStringRef &msg) "Invalid argument\\n"; } +static QString mainFunction(const DebuggerRunParameters &rp) +{ + return QLatin1String(rp.toolChainAbi.os() == Abi::WindowsOS && !rp.useTerminal ? "qMain" : "main"); +} + /////////////////////////////////////////////////////////////////////// // // Debuginfo Taskhandler @@ -1182,7 +1187,7 @@ void GdbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages if (!(languages & CppLanguage)) return; QTC_CHECK(acceptsDebuggerCommands()); - runCommand({command}); + runCommand({command, NativeCommand}); } // This is triggered when switching snapshots. @@ -2372,10 +2377,8 @@ QString GdbEngine::breakpointLocation(const BreakpointParameters &data) return QLatin1String("__cxa_throw"); if (data.type == BreakpointAtCatch) return QLatin1String("__cxa_begin_catch"); - if (data.type == BreakpointAtMain) { - const Abi abi = runParameters().toolChainAbi; - return QLatin1String(abi.os() == Abi::WindowsOS ? "qMain" : "main"); - } + if (data.type == BreakpointAtMain) + return mainFunction(runParameters()); if (data.type == BreakpointByFunction) return '"' + data.functionName + '"'; if (data.type == BreakpointByAddress) @@ -4153,11 +4156,8 @@ void GdbEngine::handleInferiorPrepared() } //runCommand("set follow-exec-mode new"); - if (rp.breakOnMain) { - QString cmd = "tbreak "; - cmd += QLatin1String(rp.toolChainAbi.os() == Abi::WindowsOS ? "qMain" : "main"); - runCommand({cmd}); - } + if (rp.breakOnMain) + runCommand({"tbreak " + mainFunction(rp)}); // Initial attempt to set breakpoints. if (rp.startMode != AttachCore) { diff --git a/src/plugins/debugger/gdb/termgdbadapter.cpp b/src/plugins/debugger/gdb/termgdbadapter.cpp index 359f26f831..e1ace5fca8 100644 --- a/src/plugins/debugger/gdb/termgdbadapter.cpp +++ b/src/plugins/debugger/gdb/termgdbadapter.cpp @@ -186,6 +186,7 @@ void GdbTermEngine::interruptInferior2() void GdbTermEngine::stubError(const QString &msg) { Core::AsynchronousMessageBox::critical(tr("Debugger Error"), msg); + notifyEngineIll(); } void GdbTermEngine::stubExited() diff --git a/src/plugins/diffeditor/differ.cpp b/src/plugins/diffeditor/differ.cpp index a99b14a127..1e452a7d46 100644 --- a/src/plugins/diffeditor/differ.cpp +++ b/src/plugins/diffeditor/differ.cpp @@ -89,6 +89,7 @@ static QList<Diff> decode(const QList<Diff> &diffList, const QStringList &lines) { QList<Diff> newDiffList; + newDiffList.reserve(diffList.count()); for (int i = 0; i < diffList.count(); i++) { Diff diff = diffList.at(i); QString text; diff --git a/src/plugins/genericprojectmanager/genericproject.cpp b/src/plugins/genericprojectmanager/genericproject.cpp index d125b58889..97d884c871 100644 --- a/src/plugins/genericprojectmanager/genericproject.cpp +++ b/src/plugins/genericprojectmanager/genericproject.cpp @@ -126,9 +126,13 @@ public: bool showInSimpleTree() const override { return true; } - QList<ProjectExplorer::ProjectAction> supportedActions(Node *) const override + bool supportsAction(ProjectAction action, Node *) const override { - return {AddNewFile, AddExistingFile, AddExistingDirectory, RemoveFile, Rename}; + return action == AddNewFile + || action == AddExistingFile + || action == AddExistingDirectory + || action == RemoveFile + || action == Rename; } bool addFiles(const QStringList &filePaths, QStringList * = 0) override @@ -164,6 +168,7 @@ GenericProject::GenericProject(const Utils::FileName &fileName) : setId(Constants::GENERICPROJECT_ID); setProjectContext(Context(GenericProjectManager::Constants::PROJECTCONTEXT)); setProjectLanguages(Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); + setDisplayName(fileName.toFileInfo().completeBaseName()); const QFileInfo fileInfo = projectFilePath().toFileInfo(); const QDir dir = fileInfo.dir(); @@ -467,11 +472,6 @@ void GenericProject::activeBuildConfigurationWasChanged() refresh(Everything); } -QString GenericProject::displayName() const -{ - return projectFilePath().toFileInfo().completeBaseName(); -} - QStringList GenericProject::buildTargets() const { const QStringList targets = { "all", "clean" }; diff --git a/src/plugins/genericprojectmanager/genericproject.h b/src/plugins/genericprojectmanager/genericproject.h index 118ec01f41..2a294ba917 100644 --- a/src/plugins/genericprojectmanager/genericproject.h +++ b/src/plugins/genericprojectmanager/genericproject.h @@ -40,8 +40,6 @@ public: explicit GenericProject(const Utils::FileName &filename); ~GenericProject() override; - QString displayName() const override; - QStringList buildTargets() const; bool addFiles(const QStringList &filePaths); diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index 7fb0790cbf..1e730091ac 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -2187,7 +2187,7 @@ bool GitClient::tryLauchingGitK(const QProcessEnvironment &env, if (HostOsInfo::isWindowsHost()) { // If git/bin is in path, use 'wish' shell to run. Otherwise (git/cmd), directly run gitk QString wish = gitBinDirectory + "/wish"; - if (QFileInfo(wish + ".exe").exists()) { + if (QFileInfo::exists(wish + ".exe")) { arguments << binary; binary = wish; } diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp index d3253dd19d..4e30062424 100644 --- a/src/plugins/git/gitplugin.cpp +++ b/src/plugins/git/gitplugin.cpp @@ -241,24 +241,13 @@ QAction *GitPlugin::createFileAction(ActionContainer *ac, return action; } -QAction *GitPlugin::createFileAction(ActionContainer *ac, const QString &defaultText, - const QString ¶meterText, Id id, const Context &context, - bool addToLocator, void (GitPlugin::*func)(), - const QKeySequence &keys) -{ - return createFileAction(ac, defaultText, parameterText, id, context, addToLocator, - [this, func]() { return (this->*func)(); }, keys); -} - QAction *GitPlugin::createProjectAction(ActionContainer *ac, const QString &defaultText, const QString ¶meterText, Id id, const Context &context, bool addToLocator, void (GitPlugin::*func)(), const QKeySequence &keys) { ParameterAction *action = createParameterAction(ac, defaultText, parameterText, id, context, - addToLocator, - [this, func]() { return (this->*func)(); }, - keys); + addToLocator, std::bind(func, this), keys); m_projectActions.push_back(action); return action; } @@ -279,7 +268,8 @@ QAction *GitPlugin::createChangeRelatedRepositoryAction(const QString &text, Id const Context &context) { return createRepositoryAction(nullptr, text, id, context, true, - [this, id] { startChangeRelatedAction(id); }, QKeySequence()); + std::bind(&GitPlugin::startChangeRelatedAction, this, id), + QKeySequence()); } // Action to act on the repository forwarded to a git client member function @@ -345,33 +335,33 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage) gitContainer->addMenu(currentFileMenu); createFileAction(currentFileMenu, tr("Diff Current File"), tr("Diff of \"%1\""), - "Git.Diff", context, true, &GitPlugin::diffCurrentFile, + "Git.Diff", context, true, std::bind(&GitPlugin::diffCurrentFile, this), QKeySequence(UseMacShortcuts ? tr("Meta+G,Meta+D") : tr("Alt+G,Alt+D"))); createFileAction(currentFileMenu, tr("Log Current File"), tr("Log of \"%1\""), - "Git.Log", context, true, &GitPlugin::logFile, + "Git.Log", context, true, std::bind(&GitPlugin::logFile, this), QKeySequence(UseMacShortcuts ? tr("Meta+G,Meta+L") : tr("Alt+G,Alt+L"))); createFileAction(currentFileMenu, tr("Blame Current File"), tr("Blame for \"%1\""), - "Git.Blame", context, true, &GitPlugin::blameFile, + "Git.Blame", context, true, std::bind(&GitPlugin::blameFile, this), QKeySequence(UseMacShortcuts ? tr("Meta+G,Meta+B") : tr("Alt+G,Alt+B"))); currentFileMenu->addSeparator(context); createFileAction(currentFileMenu, tr("Stage File for Commit"), tr("Stage \"%1\" for Commit"), - "Git.Stage", context, true, &GitPlugin::stageFile, + "Git.Stage", context, true, std::bind(&GitPlugin::stageFile, this), QKeySequence(UseMacShortcuts ? tr("Meta+G,Meta+A") : tr("Alt+G,Alt+A"))); createFileAction(currentFileMenu, tr("Unstage File from Commit"), tr("Unstage \"%1\" from Commit"), - "Git.Unstage", context, true, &GitPlugin::unstageFile); + "Git.Unstage", context, true, std::bind(&GitPlugin::unstageFile, this)); createFileAction(currentFileMenu, tr("Undo Unstaged Changes"), tr("Undo Unstaged Changes for \"%1\""), "Git.UndoUnstaged", context, - true, [this]() { return undoFileChanges(false); }); + true, std::bind(&GitPlugin::undoFileChanges, this, false)); createFileAction(currentFileMenu, tr("Undo Uncommitted Changes"), tr("Undo Uncommitted Changes for \"%1\""), "Git.Undo", context, - true, [this]() { return undoFileChanges(true); }, + true, std::bind(&GitPlugin::undoFileChanges, this, true), QKeySequence(UseMacShortcuts ? tr("Meta+G,Meta+U") : tr("Alt+G,Alt+U"))); @@ -401,7 +391,7 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage) context, true, &GitClient::diffRepository); createRepositoryAction(localRepositoryMenu, tr("Log"), "Git.LogRepository", - context, true, [this] { logRepository(); }); + context, true, std::bind(&GitPlugin::logRepository, this)); createRepositoryAction(localRepositoryMenu, tr("Reflog"), "Git.ReflogRepository", context, true, &GitClient::reflog); @@ -416,77 +406,85 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage) localRepositoryMenu->addSeparator(context); createRepositoryAction(localRepositoryMenu, tr("Commit..."), "Git.Commit", - context, true, [this] { startCommit(); }, + context, true, std::bind(&GitPlugin::startCommit, this, SimpleCommit), QKeySequence(UseMacShortcuts ? tr("Meta+G,Meta+C") : tr("Alt+G,Alt+C"))); createRepositoryAction(localRepositoryMenu, tr("Amend Last Commit..."), "Git.AmendCommit", - context, true, [this] { startAmendCommit(); }); + context, true, std::bind(&GitPlugin::startCommit, this, AmendCommit)); m_fixupCommitAction = createRepositoryAction(localRepositoryMenu, tr("Fixup Previous Commit..."), "Git.FixupCommit", context, true, - [this] { startFixupCommit(); }); + std::bind(&GitPlugin::startCommit, this, FixupCommit)); // -------------- localRepositoryMenu->addSeparator(context); createRepositoryAction(localRepositoryMenu, tr("Reset..."), "Git.Reset", - context, true, [this] { resetRepository(); }); + context, true, std::bind(&GitPlugin::resetRepository, this)); m_interactiveRebaseAction = createRepositoryAction(localRepositoryMenu, tr("Interactive Rebase..."), "Git.InteractiveRebase", - context, true, [this] { startRebase(); }); + context, true, std::bind(&GitPlugin::startRebase, this)); m_submoduleUpdateAction = createRepositoryAction(localRepositoryMenu, tr("Update Submodules"), "Git.SubmoduleUpdate", - context, true, [this] { updateSubmodules(); }); + context, true, std::bind(&GitPlugin::updateSubmodules, this)); m_abortMergeAction = createRepositoryAction(localRepositoryMenu, tr("Abort Merge"), "Git.MergeAbort", - context, true, [this] { continueOrAbortCommand(); }); + context, true, + std::bind(&GitPlugin::continueOrAbortCommand, this)); m_abortRebaseAction = createRepositoryAction(localRepositoryMenu, tr("Abort Rebase"), "Git.RebaseAbort", - context, true, [this] { continueOrAbortCommand(); }); + context, true, + std::bind(&GitPlugin::continueOrAbortCommand, this)); m_abortCherryPickAction = createRepositoryAction(localRepositoryMenu, tr("Abort Cherry Pick"), "Git.CherryPickAbort", - context, true, [this] { continueOrAbortCommand(); }); + context, true, + std::bind(&GitPlugin::continueOrAbortCommand, this)); m_abortRevertAction = createRepositoryAction(localRepositoryMenu, tr("Abort Revert"), "Git.RevertAbort", - context, true, [this] { continueOrAbortCommand(); }); + context, true, + std::bind(&GitPlugin::continueOrAbortCommand, this)); m_continueRebaseAction = createRepositoryAction(localRepositoryMenu, tr("Continue Rebase"), "Git.RebaseContinue", - context, true, [this] { continueOrAbortCommand(); }); + context, true, + std::bind(&GitPlugin::continueOrAbortCommand, this)); m_skipRebaseAction = createRepositoryAction(localRepositoryMenu, tr("Skip Rebase"), "Git.RebaseSkip", - context, true, [this] { continueOrAbortCommand(); }); + context, true, + std::bind(&GitPlugin::continueOrAbortCommand, this)); m_continueCherryPickAction = createRepositoryAction(localRepositoryMenu, tr("Continue Cherry Pick"), "Git.CherryPickContinue", - context, true, [this] { continueOrAbortCommand(); }); + context, true, + std::bind(&GitPlugin::continueOrAbortCommand, this)); m_continueRevertAction = createRepositoryAction(localRepositoryMenu, tr("Continue Revert"), "Git.RevertContinue", - context, true, [this] { continueOrAbortCommand(); }); + context, true, + std::bind(&GitPlugin::continueOrAbortCommand, this)); // -------------- localRepositoryMenu->addSeparator(context); createRepositoryAction(localRepositoryMenu, tr("Branches..."), "Git.BranchList", - context, true, [this] { branchList(); }); + context, true, std::bind(&GitPlugin::branchList, this)); // -------------- localRepositoryMenu->addSeparator(context); @@ -501,9 +499,9 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage) = createParameterAction(patchMenu, tr("Apply from Editor"), tr("Apply \"%1\""), "Git.ApplyCurrentFilePatch", - context, true, [this] { applyCurrentFilePatch(); }); + context, true, std::bind(&GitPlugin::applyCurrentFilePatch, this)); createRepositoryAction(patchMenu, tr("Apply from File..."), "Git.ApplyPatch", - context, true, [this] { promptApplyPatch(); }); + context, true, std::bind(&GitPlugin::promptApplyPatch, this)); // "Stash" menu ActionContainer *stashMenu = ActionManager::createMenu("Git.StashMenu"); @@ -511,27 +509,27 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage) localRepositoryMenu->addMenu(stashMenu); createRepositoryAction(stashMenu, tr("Stashes..."), "Git.StashList", - context, false, [this] { stashList(); }); + context, false, std::bind(&GitPlugin::stashList, this)); stashMenu->addSeparator(context); QAction *action = createRepositoryAction(stashMenu, tr("Stash"), "Git.Stash", - context, true, [this] { stash(); }); + context, true, std::bind(&GitPlugin::stash, this, false)); action->setToolTip(tr("Saves the current state of your work and resets the repository.")); action = createRepositoryAction(stashMenu, tr("Stash Unstaged Files"), "Git.StashUnstaged", - context, true, [this] { stashUnstaged(); }); + context, true, std::bind(&GitPlugin::stashUnstaged, this)); action->setToolTip(tr("Saves the current state of your unstaged files and resets the repository " "to its staged state.")); action = createRepositoryAction(stashMenu, tr("Take Snapshot..."), "Git.StashSnapshot", - context, true, [this] { stashSnapshot(); }); + context, true, std::bind(&GitPlugin::stashSnapshot, this)); action->setToolTip(tr("Saves the current state of your work.")); stashMenu->addSeparator(context); action = createRepositoryAction(stashMenu, tr("Stash Pop"), "Git.StashPop", - context, true, [this] { stashPop(); }); + context, true, std::bind(&GitPlugin::stashPop, this)); action->setToolTip(tr("Restores changes saved to the stash list using \"Stash\".")); @@ -545,13 +543,13 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage) gitContainer->addMenu(remoteRepositoryMenu); createRepositoryAction(remoteRepositoryMenu, tr("Fetch"), "Git.Fetch", - context, true, [this] { fetch(); }); + context, true, std::bind(&GitPlugin::fetch, this)); createRepositoryAction(remoteRepositoryMenu, tr("Pull"), "Git.Pull", - context, true, [this] { pull(); }); + context, true, std::bind(&GitPlugin::pull, this)); createRepositoryAction(remoteRepositoryMenu, tr("Push"), "Git.Push", - context, true, [this] { push(); }); + context, true, std::bind(&GitPlugin::push, this)); // -------------- remoteRepositoryMenu->addSeparator(context); @@ -571,7 +569,7 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage) remoteRepositoryMenu->addSeparator(context); createRepositoryAction(remoteRepositoryMenu, tr("Manage Remotes..."), "Git.RemoteList", - context, false, [this] { remoteList(); }); + context, false, std::bind(&GitPlugin::remoteList, this)); /* \"Remote Repository" menu */ @@ -583,8 +581,10 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage) createChangeRelatedRepositoryAction(tr("Cherry Pick..."), "Git.CherryPick", context); createChangeRelatedRepositoryAction(tr("Checkout..."), "Git.Checkout", context); - createRepositoryAction(0, tr("Rebase..."), "Git.Rebase", context, true, [this] { branchList(); }); - createRepositoryAction(0, tr("Merge..."), "Git.Merge", context, true, [this] { branchList(); }); + createRepositoryAction(nullptr, tr("Rebase..."), "Git.Rebase", context, true, + std::bind(&GitPlugin::branchList, this)); + createRepositoryAction(nullptr, tr("Merge..."), "Git.Merge", context, true, + std::bind(&GitPlugin::branchList, this)); /* \Actions only in locator */ // -------------- @@ -598,16 +598,16 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage) context, true, &GitClient::launchGitK); createFileAction(gitToolsMenu, tr("Gitk Current File"), tr("Gitk of \"%1\""), - "Git.GitkFile", context, true, &GitPlugin::gitkForCurrentFile); + "Git.GitkFile", context, true, std::bind(&GitPlugin::gitkForCurrentFile, this)); createFileAction(gitToolsMenu, tr("Gitk for folder of Current File"), tr("Gitk for folder of \"%1\""), - "Git.GitkFolder", context, true, &GitPlugin::gitkForCurrentFolder); + "Git.GitkFolder", context, true, std::bind(&GitPlugin::gitkForCurrentFolder, this)); // -------------- gitToolsMenu->addSeparator(context); createRepositoryAction(gitToolsMenu, tr("Git Gui"), "Git.GitGui", - context, true, [this] { gitGui(); }); + context, true, std::bind(&GitPlugin::gitGui, this)); // -------------- gitToolsMenu->addSeparator(context); @@ -619,7 +619,7 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage) m_mergeToolAction = createRepositoryAction(gitToolsMenu, tr("Merge Tool"), "Git.MergeTool", - context, true, [this] { startMergeTool(); }); + context, true, std::bind(&GitPlugin::startMergeTool, this)); /* \"Git Tools" menu */ @@ -921,21 +921,6 @@ void GitPlugin::gitGui() m_gitClient->launchGitGui(state.topLevel()); } -void GitPlugin::startAmendCommit() -{ - startCommit(AmendCommit); -} - -void GitPlugin::startFixupCommit() -{ - startCommit(FixupCommit); -} - -void GitPlugin::startCommit() -{ - startCommit(SimpleCommit); -} - void GitPlugin::startCommit(CommitType commitType) { if (raiseSubmitEditor()) diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h index 5948c3b293..790f0959a8 100644 --- a/src/plugins/git/gitplugin.h +++ b/src/plugins/git/gitplugin.h @@ -89,7 +89,7 @@ public: bool isCommitEditorOpen() const; static QString msgRepositoryLabel(const QString &repository); static QString invalidBranchAndRemoteNamePattern(); - void startCommit(); + void startCommit(CommitType commitType = SimpleCommit); void updateBranches(const QString &repository); protected: @@ -128,8 +128,6 @@ private: void applyCurrentFilePatch(); void promptApplyPatch(); - void startAmendCommit(); - void startFixupCommit(); void stash(bool unstagedOnly = false); void stashUnstaged(); void stashSnapshot(); @@ -160,11 +158,6 @@ private: Core::Id id, const Core::Context &context, bool addToLocator, const std::function<void()> &callback, const QKeySequence &keys = QKeySequence()); - QAction *createFileAction(Core::ActionContainer *ac, - const QString &defaultText, const QString ¶meterText, - Core::Id id, const Core::Context &context, bool addToLocator, - void (GitPlugin::*func)(), - const QKeySequence &keys = QKeySequence()); QAction *createProjectAction(Core::ActionContainer *ac, const QString &defaultText, const QString ¶meterText, @@ -188,7 +181,6 @@ private: void cleanCommitMessageFile(); void cleanRepository(const QString &directory); void applyPatch(const QString &workingDirectory, QString file = QString()); - void startCommit(CommitType commitType); void updateVersionWarning(); Core::CommandLocator *m_commandLocator = nullptr; diff --git a/src/plugins/nim/project/nimproject.cpp b/src/plugins/nim/project/nimproject.cpp index 39c8ac3e80..fe58ea5c37 100644 --- a/src/plugins/nim/project/nimproject.cpp +++ b/src/plugins/nim/project/nimproject.cpp @@ -57,6 +57,7 @@ const int MIN_TIME_BETWEEN_PROJECT_SCANS = 4500; NimProject::NimProject(const FileName &fileName) : Project(Constants::C_NIM_MIMETYPE, fileName) { setId(Constants::C_NIMPROJECT_ID); + setDisplayName(fileName.toFileInfo().completeBaseName()); m_projectScanTimer.setSingleShot(true); connect(&m_projectScanTimer, &QTimer::timeout, this, &NimProject::collectProjectFiles); @@ -66,11 +67,6 @@ NimProject::NimProject(const FileName &fileName) : Project(Constants::C_NIM_MIME collectProjectFiles(); } -QString NimProject::displayName() const -{ - return projectFilePath().toFileInfo().completeBaseName(); -} - bool NimProject::needsConfiguration() const { return targets().empty(); @@ -159,12 +155,12 @@ bool NimProject::supportsKit(Kit *k, QString *errorMessage) const auto tc = dynamic_cast<NimToolChain*>(ToolChainKitInformation::toolChain(k, Constants::C_NIMLANGUAGE_ID)); if (!tc) { if (errorMessage) - *errorMessage = tr("No nim compiler set."); + *errorMessage = tr("No Nim compiler set."); return false; } if (!tc->compilerCommand().exists()) { if (errorMessage) - *errorMessage = tr("Nim compiler doesn't exist"); + *errorMessage = tr("Nim compiler does not exist"); return false; } return true; diff --git a/src/plugins/nim/project/nimproject.h b/src/plugins/nim/project/nimproject.h index 5072ce478d..50e03a3fa3 100644 --- a/src/plugins/nim/project/nimproject.h +++ b/src/plugins/nim/project/nimproject.h @@ -41,7 +41,6 @@ class NimProject : public ProjectExplorer::Project public: explicit NimProject(const Utils::FileName &fileName); - QString displayName() const override; bool needsConfiguration() const override; bool supportsKit(ProjectExplorer::Kit *k, QString *errorMessage) const override; Utils::FileNameList nimFiles() const; diff --git a/src/plugins/nim/project/nimprojectnode.cpp b/src/plugins/nim/project/nimprojectnode.cpp index 5c22cea185..223c6f950d 100644 --- a/src/plugins/nim/project/nimprojectnode.cpp +++ b/src/plugins/nim/project/nimprojectnode.cpp @@ -37,23 +37,19 @@ NimProjectNode::NimProjectNode(NimProject &project, , m_project(project) {} -QList<ProjectAction> NimProjectNode::supportedActions(Node *node) const +bool NimProjectNode::supportsAction(ProjectAction action, Node *node) const { - static const QList<ProjectAction> fileActions = {ProjectAction::Rename, - ProjectAction::RemoveFile - }; - static const QList<ProjectAction> folderActions = {ProjectAction::AddNewFile, - ProjectAction::RemoveFile, - ProjectAction::AddExistingFile - }; switch (node->nodeType()) { case NodeType::File: - return fileActions; + return action == ProjectAction::Rename + || action == ProjectAction::RemoveFile; case NodeType::Folder: case NodeType::Project: - return folderActions; + return action == ProjectAction::AddNewFile + || action == ProjectAction::RemoveFile + || action == ProjectAction::AddExistingFile; default: - return ProjectNode::supportedActions(node); + return ProjectNode::supportsAction(action, node); } } diff --git a/src/plugins/nim/project/nimprojectnode.h b/src/plugins/nim/project/nimprojectnode.h index ba4dd63415..05270846f9 100644 --- a/src/plugins/nim/project/nimprojectnode.h +++ b/src/plugins/nim/project/nimprojectnode.h @@ -38,7 +38,7 @@ class NimProjectNode : public ProjectExplorer::ProjectNode public: NimProjectNode(NimProject &project, const Utils::FileName &projectFilePath); - QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const override; + bool supportsAction(ProjectExplorer::ProjectAction action, Node *node) const override; bool addFiles(const QStringList &filePaths, QStringList *) override; bool removeFiles(const QStringList &filePaths, QStringList *) override; bool deleteFiles(const QStringList &) override; diff --git a/src/plugins/projectexplorer/abi.cpp b/src/plugins/projectexplorer/abi.cpp index fa504ca3b8..7c5295fac5 100644 --- a/src/plugins/projectexplorer/abi.cpp +++ b/src/plugins/projectexplorer/abi.cpp @@ -545,7 +545,7 @@ Abi Abi::abiFromTargetTriplet(const QString &triple) format = Abi::ElfFormat; } else if (p == QLatin1String("mingw32") || p == QLatin1String("win32") || p == QLatin1String("mingw32msvc") || p == QLatin1String("msys") - || p == QLatin1String("cygwin")) { + || p == QLatin1String("cygwin") || p == QLatin1String("windows")) { arch = Abi::X86Architecture; os = Abi::WindowsOS; flavor = Abi::WindowsMSysFlavor; @@ -789,6 +789,28 @@ QList<Abi::OSFlavor> Abi::flavorsForOs(const Abi::OS &o) return result; } +Abi::OSFlavor Abi::flavorForMsvcVersion(int version) +{ + if (version >= 1910) + return WindowsMsvc2017Flavor; + switch (version) { + case 1900: + return WindowsMsvc2015Flavor; + case 1800: + return WindowsMsvc2013Flavor; + case 1700: + return WindowsMsvc2012Flavor; + case 1600: + return WindowsMsvc2010Flavor; + case 1500: + return WindowsMsvc2008Flavor; + case 1400: + return WindowsMsvc2005Flavor; + default: + return WindowsMSysFlavor; + } +} + Abi Abi::hostAbi() { Architecture arch = QTC_CPU; // define set by qmake @@ -798,20 +820,8 @@ Abi Abi::hostAbi() #if defined (Q_OS_WIN) os = WindowsOS; -#if _MSC_VER >= 1910 - subos = WindowsMsvc2017Flavor; -#elif _MSC_VER == 1900 - subos = WindowsMsvc2015Flavor; -#elif _MSC_VER == 1800 - subos = WindowsMsvc2013Flavor; -#elif _MSC_VER == 1700 - subos = WindowsMsvc2012Flavor; -#elif _MSC_VER == 1600 - subos = WindowsMsvc2010Flavor; -#elif _MSC_VER == 1500 - subos = WindowsMsvc2008Flavor; -#elif _MSC_VER == 1400 - subos = WindowsMsvc2005Flavor; +#ifdef _MSC_VER + subos = flavorForMsvcVersion(_MSC_VER); #elif defined (Q_CC_MINGW) subos = WindowsMSysFlavor; #endif @@ -1133,6 +1143,10 @@ void ProjectExplorer::ProjectExplorerPlugin::testAbiFromTargetTriplet_data() << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor) << int(Abi::PEFormat) << 64; + QTest::newRow("x86-pc-windows-msvc") << int(Abi::X86Architecture) + << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor) + << int(Abi::PEFormat) << 32; + QTest::newRow("mingw32") << int(Abi::X86Architecture) << int(Abi::WindowsOS) << int(Abi::WindowsMSysFlavor) << int(Abi::PEFormat) << 0; diff --git a/src/plugins/projectexplorer/abi.h b/src/plugins/projectexplorer/abi.h index 77e769131e..06157aa81b 100644 --- a/src/plugins/projectexplorer/abi.h +++ b/src/plugins/projectexplorer/abi.h @@ -139,6 +139,7 @@ public: static QString toString(int w); static QList<OSFlavor> flavorsForOs(const OS &o); + static OSFlavor flavorForMsvcVersion(int version); static Abi hostAbi(); static QList<Abi> abisOfBinary(const Utils::FileName &path); diff --git a/src/plugins/projectexplorer/abiwidget.cpp b/src/plugins/projectexplorer/abiwidget.cpp index 278a37a378..5c7671f37f 100644 --- a/src/plugins/projectexplorer/abiwidget.cpp +++ b/src/plugins/projectexplorer/abiwidget.cpp @@ -190,6 +190,7 @@ void AbiWidget::setAbis(const QList<Abi> &abiList, const Abi ¤t) QList<Abi> AbiWidget::supportedAbis() const { QList<Abi> result; + result.reserve(d->m_abi->count()); for (int i = 1; i < d->m_abi->count(); ++i) result << Abi(d->m_abi->itemData(i).toString()); return result; diff --git a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp index b1792e6fd5..39b17a6444 100644 --- a/src/plugins/projectexplorer/devicesupport/devicemanager.cpp +++ b/src/plugins/projectexplorer/devicesupport/devicemanager.cpp @@ -357,7 +357,7 @@ DeviceManager::DeviceManager(bool isInstance) : d(new DeviceManagerPrivate) m_instance = this; d->hostKeyDatabase = QSsh::SshHostKeyDatabasePtr::create(); const QString keyFilePath = hostKeysFilePath(); - if (QFileInfo(keyFilePath).exists()) { + if (QFileInfo::exists(keyFilePath)) { QString error; if (!d->hostKeyDatabase->load(keyFilePath, &error)) Core::MessageManager::write(error); diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index ae4c9813d8..8bb25b5a6d 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -269,11 +269,19 @@ static QList<Abi> guessGccAbi(const QString &m, const QByteArray ¯os) Abi::OSFlavor flavor = guessed.osFlavor(); Abi::BinaryFormat format = guessed.binaryFormat(); int width = guessed.wordWidth(); + const QByteArray mscVer = "#define _MSC_VER "; if (macros.contains("#define __SIZEOF_SIZE_T__ 8")) width = 64; else if (macros.contains("#define __SIZEOF_SIZE_T__ 4")) width = 32; + int mscVerIndex = macros.indexOf(mscVer); + if (mscVerIndex != -1) { + mscVerIndex += mscVer.length(); + const int eol = macros.indexOf('\n', mscVerIndex); + const int msvcVersion = macros.mid(mscVerIndex, eol - mscVerIndex).toInt(); + flavor = Abi::flavorForMsvcVersion(msvcVersion); + } if (os == Abi::DarwinOS) { // Apple does PPC and x86! diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp index 3c2a247e9a..9b97254f60 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp @@ -327,7 +327,7 @@ void JsonWizard::openFiles(const JsonWizard::GeneratorFiles &files) bool openedSomething = false; foreach (const JsonWizard::GeneratorFile &f, files) { const Core::GeneratedFile &file = f.file; - if (!QFileInfo(file.path()).exists()) { + if (!QFileInfo::exists(file.path())) { errorMessage = QCoreApplication::translate("ProjectExplorer::JsonWizard", "\"%1\" does not exist in the file system.") .arg(QDir::toNativeSeparators(file.path())); diff --git a/src/plugins/projectexplorer/msvcparser.cpp b/src/plugins/projectexplorer/msvcparser.cpp index 25e0f3131d..ae7a621f92 100644 --- a/src/plugins/projectexplorer/msvcparser.cpp +++ b/src/plugins/projectexplorer/msvcparser.cpp @@ -30,24 +30,24 @@ #include <utils/qtcassert.h> #include <utils/fileutils.h> +using namespace Utils; + // As of MSVC 2015: "foo.cpp(42) :" -> "foo.cpp(42):" -static const char FILE_POS_PATTERN[] = "(?:\\d+>)?(cl|LINK|.+[^ ]) ?: "; -static const char ERROR_PATTERN[] = "[A-Z]+\\d\\d\\d\\d ?:"; +static const char FILE_POS_PATTERN[] = "^(?:\\d+>)?(cl|LINK|.+[^ ]) ?: "; -static QPair<Utils::FileName, int> parseFileName(const QString &input) +static QPair<FileName, int> parseFileName(const QString &input) { QString fileName = input; - if (fileName.startsWith(QLatin1String("LINK")) - || fileName.startsWith(QLatin1String("cl"))) - return qMakePair(Utils::FileName(), -1); + if (fileName.startsWith("LINK") || fileName.startsWith("cl")) + return qMakePair(FileName(), -1); // Extract linenumber (if it is there): int linenumber = -1; - if (fileName.endsWith(QLatin1Char(')'))) { - int pos = fileName.lastIndexOf(QLatin1Char('(')); + if (fileName.endsWith(')')) { + int pos = fileName.lastIndexOf('('); if (pos >= 0) { // clang-cl gives column, too: "foo.cpp(34,1)" as opposed to MSVC "foo.cpp(34)". - int endPos = fileName.indexOf(QLatin1Char(','), pos + 1); + int endPos = fileName.indexOf(',', pos + 1); if (endPos < 0) endPos = fileName.size() - 1; bool ok = false; @@ -58,8 +58,8 @@ static QPair<Utils::FileName, int> parseFileName(const QString &input) } } } - const QString normalized = Utils::FileUtils::normalizePathName(fileName); - return qMakePair(Utils::FileName::fromUserInput(normalized), linenumber); + const QString normalized = FileUtils::normalizePathName(fileName); + return qMakePair(FileName::fromUserInput(normalized), linenumber); } using namespace ProjectExplorer; @@ -68,9 +68,9 @@ using namespace ProjectExplorer; static bool handleNmakeJomMessage(const QString &line, Task *task) { int matchLength = 0; - if (line.startsWith(QLatin1String("Error:"))) + if (line.startsWith("Error:")) matchLength = 6; - else if (line.startsWith(QLatin1String("Warning:"))) + else if (line.startsWith("Warning:")) matchLength = 8; if (!matchLength) @@ -78,7 +78,7 @@ static bool handleNmakeJomMessage(const QString &line, Task *task) *task = Task(Task::Error, line.mid(matchLength).trimmed(), /* description */ - Utils::FileName(), /* fileName */ + FileName(), /* fileName */ -1, /* linenumber */ Constants::TASK_CATEGORY_COMPILE); return true; @@ -87,32 +87,32 @@ static bool handleNmakeJomMessage(const QString &line, Task *task) static Task::TaskType taskType(const QString &category) { Task::TaskType type = Task::Unknown; - if (category == QLatin1String("warning")) + if (category == "warning") type = Task::Warning; - else if (category == QLatin1String("error")) + else if (category == "error") type = Task::Error; return type; } MsvcParser::MsvcParser() { - setObjectName(QLatin1String("MsvcParser")); - m_compileRegExp.setPattern(QString::fromLatin1("^") + QLatin1String(FILE_POS_PATTERN) - + QLatin1String("(Command line |fatal )?(warning|error) (") - + QLatin1String(ERROR_PATTERN) + QLatin1String(".*)$")); + setObjectName("MsvcParser"); + m_compileRegExp.setPattern(QString(FILE_POS_PATTERN) + + "(?:Command line |fatal )?(?:(warning|error) " + "([A-Z]+\\d{4} ?: )|note: )(.*)$"); QTC_CHECK(m_compileRegExp.isValid()); - m_additionalInfoRegExp.setPattern(QString::fromLatin1("^ (?:(could be |or )\\s*')?(.*)\\((\\d+)\\) : (.*)$")); + m_additionalInfoRegExp.setPattern("^ (?:(could be |or )\\s*')?(.*)\\((\\d+)\\) : (.*)$"); QTC_CHECK(m_additionalInfoRegExp.isValid()); } void MsvcParser::stdOutput(const QString &line) { QRegularExpressionMatch match = m_additionalInfoRegExp.match(line); - if (line.startsWith(QLatin1String(" ")) && !match.hasMatch()) { + if (line.startsWith(" ") && !match.hasMatch()) { if (m_lastTask.isNull()) return; - m_lastTask.description.append(QLatin1Char('\n')); + m_lastTask.description.append('\n'); m_lastTask.description.append(line.mid(8)); // trim trailing spaces: int i = 0; @@ -124,7 +124,7 @@ void MsvcParser::stdOutput(const QString &line) if (m_lastTask.formats.isEmpty()) { QTextLayout::FormatRange fr; - fr.start = m_lastTask.description.indexOf(QLatin1Char('\n')) + 1; + fr.start = m_lastTask.description.indexOf('\n') + 1; fr.length = m_lastTask.description.length() - fr.start; fr.format.setFontItalic(true); m_lastTask.formats.append(fr); @@ -147,7 +147,7 @@ void MsvcParser::stdOutput(const QString &line) if (!match.captured(1).isEmpty()) description.chop(1); // Remove trailing quote m_lastTask = Task(Task::Unknown, description, - Utils::FileName::fromUserInput(match.captured(2)), /* fileName */ + FileName::fromUserInput(match.captured(2)), /* fileName */ match.captured(3).toInt(), /* linenumber */ Constants::TASK_CATEGORY_COMPILE); m_lines = 1; @@ -174,9 +174,9 @@ bool MsvcParser::processCompileLine(const QString &line) QRegularExpressionMatch match = m_compileRegExp.match(line); if (match.hasMatch()) { - QPair<Utils::FileName, int> position = parseFileName(match.captured(1)); - m_lastTask = Task(taskType(match.captured(3)), - match.captured(4).trimmed() /* description */, + QPair<FileName, int> position = parseFileName(match.captured(1)); + m_lastTask = Task(taskType(match.captured(2)), + match.captured(3) + match.captured(4).trimmed(), // description position.first, position.second, Constants::TASK_CATEGORY_COMPILE); m_lines = 1; @@ -204,15 +204,13 @@ void MsvcParser::doFlush() // ".\qwindowsgdinativeinterface.cpp(48,3) : error: unknown type name 'errr'" static inline QString clangClCompilePattern() { - return QLatin1Char('^') + QLatin1String(FILE_POS_PATTERN) - + QLatin1String(" (warning|error): (") - + QLatin1String(".*)$"); + return QLatin1String(FILE_POS_PATTERN) + " (warning|error): (.*)$"; } ClangClParser::ClangClParser() - : m_compileRegExp(clangClCompilePattern()) + : m_compileRegExp(clangClCompilePattern()) { - setObjectName(QLatin1String("ClangClParser")); + setObjectName("ClangClParser"); QTC_CHECK(m_compileRegExp.isValid()); } @@ -230,8 +228,8 @@ void ClangClParser::stdOutput(const QString &line) static inline bool isClangCodeMarker(const QString &trimmedLine) { return trimmedLine.constEnd() == - std::find_if(trimmedLine.constBegin(), trimmedLine.constEnd(), - [] (QChar c) { return c != QLatin1Char(' ') && c != QLatin1Char('^') && c != QLatin1Char('~'); }); + std::find_if(trimmedLine.constBegin(), trimmedLine.constEnd(), + [] (QChar c) { return c != ' ' && c != '^' && c != '~'; }); } void ClangClParser::stdError(const QString &lineIn) @@ -245,13 +243,13 @@ void ClangClParser::stdError(const QString &lineIn) } // Finish a sequence of warnings/errors: "2 warnings generated." - if (!line.isEmpty() && line.at(0).isDigit() && line.endsWith(QLatin1String("generated."))) { + if (!line.isEmpty() && line.at(0).isDigit() && line.endsWith("generated.")) { doFlush(); return; } // Start a new error message by a sequence of "In file included from " which is to be skipped. - if (line.startsWith(QLatin1String("In file included from "))) { + if (line.startsWith("In file included from ")) { doFlush(); return; } @@ -259,7 +257,7 @@ void ClangClParser::stdError(const QString &lineIn) QRegularExpressionMatch match = m_compileRegExp.match(line); if (match.hasMatch()) { doFlush(); - const QPair<Utils::FileName, int> position = parseFileName(match.captured(1)); + const QPair<FileName, int> position = parseFileName(match.captured(1)); m_lastTask = Task(taskType(match.captured(2)), match.captured(3).trimmed(), position.first, position.second, Constants::TASK_CATEGORY_COMPILE); @@ -273,7 +271,7 @@ void ClangClParser::stdError(const QString &lineIn) doFlush(); return; } - m_lastTask.description.append(QLatin1Char('\n')); + m_lastTask.description.append('\n'); m_lastTask.description.append(trimmed); ++m_linkedLines; return; @@ -294,13 +292,10 @@ void ClangClParser::doFlush() #ifdef WITH_TESTS # include <QTest> - # include "projectexplorer.h" - # include "projectexplorer/outputparser_test.h" -using namespace ProjectExplorer::Internal; -using namespace ProjectExplorer; +namespace ProjectExplorer { void ProjectExplorerPlugin::testMsvcOutputParsers_data() { @@ -313,240 +308,261 @@ void ProjectExplorerPlugin::testMsvcOutputParsers_data() QTest::newRow("pass-through stdout") - << QString::fromLatin1("Sometext") << OutputParserTester::STDOUT - << QString::fromLatin1("Sometext\n") << QString() + << "Sometext" << OutputParserTester::STDOUT + << "Sometext\n" << "" << QList<Task>() - << QString(); + << ""; QTest::newRow("pass-through stderr") - << QString::fromLatin1("Sometext") << OutputParserTester::STDERR - << QString() << QString::fromLatin1("Sometext\n") + << "Sometext" << OutputParserTester::STDERR + << "" << "Sometext\n" << QList<Task>() - << QString(); + << ""; QTest::newRow("labeled error") - << QString::fromLatin1("qmlstandalone\\main.cpp(54) : error C4716: 'findUnresolvedModule' : must return a value") << OutputParserTester::STDOUT - << QString() << QString() + << "qmlstandalone\\main.cpp(54) : error C4716: 'findUnresolvedModule' : must return a value" + << OutputParserTester::STDOUT + << "" << "" << (QList<Task>() << Task(Task::Error, - QLatin1String("C4716: 'findUnresolvedModule' : must return a value"), - Utils::FileName::fromUserInput(QLatin1String("qmlstandalone\\main.cpp")), 54, + "C4716: 'findUnresolvedModule' : must return a value", + FileName::fromUserInput("qmlstandalone\\main.cpp"), 54, Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << ""; QTest::newRow("labeled error-2015") - << QString::fromLatin1("qmlstandalone\\main.cpp(54): error C4716: 'findUnresolvedModule' : must return a value") << OutputParserTester::STDOUT - << QString() << QString() + << "qmlstandalone\\main.cpp(54): error C4716: 'findUnresolvedModule' : must return a value" + << OutputParserTester::STDOUT + << "" << "" << (QList<Task>() << Task(Task::Error, - QLatin1String("C4716: 'findUnresolvedModule' : must return a value"), - Utils::FileName::fromUserInput(QLatin1String("qmlstandalone\\main.cpp")), 54, + "C4716: 'findUnresolvedModule' : must return a value", + FileName::fromUserInput("qmlstandalone\\main.cpp"), 54, Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << ""; QTest::newRow("labeled error with number prefix") - << QString::fromLatin1("1>qmlstandalone\\main.cpp(54) : error C4716: 'findUnresolvedModule' : must return a value") << OutputParserTester::STDOUT - << QString() << QString() + << "1>qmlstandalone\\main.cpp(54) : error C4716: 'findUnresolvedModule' : must return a value" + << OutputParserTester::STDOUT + << "" << "" << (QList<Task>() << Task(Task::Error, - QLatin1String("C4716: 'findUnresolvedModule' : must return a value"), - Utils::FileName::fromUserInput(QLatin1String("qmlstandalone\\main.cpp")), 54, + "C4716: 'findUnresolvedModule' : must return a value", + FileName::fromUserInput("qmlstandalone\\main.cpp"), 54, Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << ""; QTest::newRow("labeled warning") - << QString::fromLatin1("x:\\src\\plugins\\projectexplorer\\msvcparser.cpp(69) : warning C4100: 'something' : unreferenced formal parameter") << OutputParserTester::STDOUT - << QString() << QString() + << "x:\\src\\plugins\\projectexplorer\\msvcparser.cpp(69) : warning C4100: 'something' : unreferenced formal parameter" + << OutputParserTester::STDOUT + << "" << "" << (QList<Task>() << Task(Task::Warning, - QLatin1String("C4100: 'something' : unreferenced formal parameter"), - Utils::FileName::fromUserInput(QLatin1String("x:\\src\\plugins\\projectexplorer\\msvcparser.cpp")), 69, + "C4100: 'something' : unreferenced formal parameter", + FileName::fromUserInput("x:\\src\\plugins\\projectexplorer\\msvcparser.cpp"), 69, Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << ""; QTest::newRow("labeled warning with number prefix") - << QString::fromLatin1("1>x:\\src\\plugins\\projectexplorer\\msvcparser.cpp(69) : warning C4100: 'something' : unreferenced formal parameter") << OutputParserTester::STDOUT - << QString() << QString() + << "1>x:\\src\\plugins\\projectexplorer\\msvcparser.cpp(69) : warning C4100: 'something' : unreferenced formal parameter" + << OutputParserTester::STDOUT + << "" << "" << (QList<Task>() << Task(Task::Warning, - QLatin1String("C4100: 'something' : unreferenced formal parameter"), - Utils::FileName::fromUserInput(QLatin1String("x:\\src\\plugins\\projectexplorer\\msvcparser.cpp")), 69, + "C4100: 'something' : unreferenced formal parameter", + FileName::fromUserInput("x:\\src\\plugins\\projectexplorer\\msvcparser.cpp"), 69, Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << ""; QTest::newRow("additional information") - << QString::fromLatin1("x:\\src\\plugins\\texteditor\\icompletioncollector.h(50) : warning C4099: 'TextEditor::CompletionItem' : type name first seen using 'struct' now seen using 'class'\n" - " x:\\src\\plugins\\texteditor\\completionsupport.h(39) : see declaration of 'TextEditor::CompletionItem'") + << "x:\\src\\plugins\\texteditor\\icompletioncollector.h(50) : warning C4099: 'TextEditor::CompletionItem' : type name first seen using 'struct' now seen using 'class'\n" + " x:\\src\\plugins\\texteditor\\completionsupport.h(39) : see declaration of 'TextEditor::CompletionItem'" << OutputParserTester::STDOUT - << QString() << QString() + << "" << "" << (QList<Task>() << Task(Task::Warning, - QLatin1String("C4099: 'TextEditor::CompletionItem' : type name first seen using 'struct' now seen using 'class'"), - Utils::FileName::fromUserInput(QLatin1String("x:\\src\\plugins\\texteditor\\icompletioncollector.h")), 50, + "C4099: 'TextEditor::CompletionItem' : type name first seen using 'struct' now seen using 'class'", + FileName::fromUserInput("x:\\src\\plugins\\texteditor\\icompletioncollector.h"), 50, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Unknown, - QLatin1String("see declaration of 'TextEditor::CompletionItem'"), - Utils::FileName::fromUserInput(QLatin1String("x:\\src\\plugins\\texteditor\\completionsupport.h")), 39, + "see declaration of 'TextEditor::CompletionItem'", + FileName::fromUserInput("x:\\src\\plugins\\texteditor\\completionsupport.h"), 39, Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << ""; QTest::newRow("additional information with prefix") - << QString::fromLatin1("2>x:\\src\\plugins\\texteditor\\icompletioncollector.h(50) : warning C4099: 'TextEditor::CompletionItem' : type name first seen using 'struct' now seen using 'class'\n" - " x:\\src\\plugins\\texteditor\\completionsupport.h(39) : see declaration of 'TextEditor::CompletionItem'") + << "2>x:\\src\\plugins\\texteditor\\icompletioncollector.h(50) : warning C4099: 'TextEditor::CompletionItem' : type name first seen using 'struct' now seen using 'class'\n" + " x:\\src\\plugins\\texteditor\\completionsupport.h(39) : see declaration of 'TextEditor::CompletionItem'" << OutputParserTester::STDOUT - << QString() << QString() + << "" << "" << (QList<Task>() << Task(Task::Warning, - QLatin1String("C4099: 'TextEditor::CompletionItem' : type name first seen using 'struct' now seen using 'class'"), - Utils::FileName::fromUserInput(QLatin1String("x:\\src\\plugins\\texteditor\\icompletioncollector.h")), 50, + "C4099: 'TextEditor::CompletionItem' : type name first seen using 'struct' now seen using 'class'", + FileName::fromUserInput("x:\\src\\plugins\\texteditor\\icompletioncollector.h"), 50, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Unknown, - QLatin1String("see declaration of 'TextEditor::CompletionItem'"), - Utils::FileName::fromUserInput(QLatin1String("x:\\src\\plugins\\texteditor\\completionsupport.h")), 39, + "see declaration of 'TextEditor::CompletionItem'", + FileName::fromUserInput("x:\\src\\plugins\\texteditor\\completionsupport.h"), 39, Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << ""; QTest::newRow("fatal linker error") - << QString::fromLatin1("LINK : fatal error LNK1146: no argument specified with option '/LIBPATH:'") + << "LINK : fatal error LNK1146: no argument specified with option '/LIBPATH:'" << OutputParserTester::STDOUT - << QString() << QString() + << "" << "" << (QList<Task>() << Task(Task::Error, - QLatin1String("LNK1146: no argument specified with option '/LIBPATH:'"), - Utils::FileName(), -1, + "LNK1146: no argument specified with option '/LIBPATH:'", + FileName(), -1, Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << ""; // This actually comes through stderr! QTest::newRow("command line warning") - << QString::fromLatin1("cl : Command line warning D9002 : ignoring unknown option '-fopenmp'") + << "cl : Command line warning D9002 : ignoring unknown option '-fopenmp'" << OutputParserTester::STDERR - << QString() << QString() + << "" << "" << (QList<Task>() << Task(Task::Warning, - QLatin1String("D9002 : ignoring unknown option '-fopenmp'"), - Utils::FileName(), -1, + "D9002 : ignoring unknown option '-fopenmp'", + FileName(), -1, Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << ""; QTest::newRow("complex error") - << QString::fromLatin1("..\\untitled\\main.cpp(19) : error C2440: 'initializing' : cannot convert from 'int' to 'std::_Tree<_Traits>::iterator'\n" - " with\n" - " [\n" - " _Traits=std::_Tmap_traits<int,double,std::less<int>,std::allocator<std::pair<const int,double>>,false>\n" - " ]\n" - " No constructor could take the source type, or constructor overload resolution was ambiguous") + << "..\\untitled\\main.cpp(19) : error C2440: 'initializing' : cannot convert from 'int' to 'std::_Tree<_Traits>::iterator'\n" + " with\n" + " [\n" + " _Traits=std::_Tmap_traits<int,double,std::less<int>,std::allocator<std::pair<const int,double>>,false>\n" + " ]\n" + " No constructor could take the source type, or constructor overload resolution was ambiguous" << OutputParserTester::STDOUT - << QString() << QString() + << "" << "" << (QList<Task>() << Task(Task::Error, - QLatin1String("C2440: 'initializing' : cannot convert from 'int' to 'std::_Tree<_Traits>::iterator'\n" - "with\n" - "[\n" - " _Traits=std::_Tmap_traits<int,double,std::less<int>,std::allocator<std::pair<const int,double>>,false>\n" - "]\n" - "No constructor could take the source type, or constructor overload resolution was ambiguous"), - Utils::FileName::fromUserInput(QLatin1String("..\\untitled\\main.cpp")), 19, + "C2440: 'initializing' : cannot convert from 'int' to 'std::_Tree<_Traits>::iterator'\n" + "with\n" + "[\n" + " _Traits=std::_Tmap_traits<int,double,std::less<int>,std::allocator<std::pair<const int,double>>,false>\n" + "]\n" + "No constructor could take the source type, or constructor overload resolution was ambiguous", + FileName::fromUserInput("..\\untitled\\main.cpp"), 19, Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << ""; QTest::newRow("Linker error 1") - << QString::fromLatin1("main.obj : error LNK2019: unresolved external symbol \"public: void __thiscall Data::doit(void)\" (?doit@Data@@QAEXXZ) referenced in function _main") + << "main.obj : error LNK2019: unresolved external symbol \"public: void __thiscall Data::doit(void)\" (?doit@Data@@QAEXXZ) referenced in function _main" << OutputParserTester::STDOUT - << QString() << QString() + << "" << "" << (QList<Task>() << Task(Task::Error, - QLatin1String("LNK2019: unresolved external symbol \"public: void __thiscall Data::doit(void)\" (?doit@Data@@QAEXXZ) referenced in function _main"), - Utils::FileName::fromUserInput(QLatin1String("main.obj")), -1, + "LNK2019: unresolved external symbol \"public: void __thiscall Data::doit(void)\" (?doit@Data@@QAEXXZ) referenced in function _main", + FileName::fromUserInput("main.obj"), -1, Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << ""; QTest::newRow("Linker error 2") - << QString::fromLatin1("debug\\Experimentation.exe : fatal error LNK1120: 1 unresolved externals") + << "debug\\Experimentation.exe : fatal error LNK1120: 1 unresolved externals" << OutputParserTester::STDOUT - << QString() << QString() + << "" << "" << (QList<Task>() << Task(Task::Error, - QLatin1String("LNK1120: 1 unresolved externals"), - Utils::FileName::fromUserInput(QLatin1String("debug\\Experimentation.exe")), -1, + "LNK1120: 1 unresolved externals", + FileName::fromUserInput("debug\\Experimentation.exe"), -1, Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << ""; QTest::newRow("nmake error") - << QString::fromLatin1("Error: dependent '..\\..\\..\\..\\creator-2.5\\src\\plugins\\coreplugin\\ifile.h' does not exist.") + << "Error: dependent '..\\..\\..\\..\\creator-2.5\\src\\plugins\\coreplugin\\ifile.h' does not exist." << OutputParserTester::STDOUT - << QString() << QString() + << "" << "" << (QList<Task>() << Task(Task::Error, - QLatin1String("dependent '..\\..\\..\\..\\creator-2.5\\src\\plugins\\coreplugin\\ifile.h' does not exist."), - Utils::FileName(), -1, + "dependent '..\\..\\..\\..\\creator-2.5\\src\\plugins\\coreplugin\\ifile.h' does not exist.", + FileName(), -1, Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << ""; QTest::newRow("jom error") - << QString::fromLatin1("Error: dependent 'main.cpp' does not exist.") + << "Error: dependent 'main.cpp' does not exist." << OutputParserTester::STDERR - << QString() << QString() + << "" << "" << (QList<Task>() << Task(Task::Error, - QLatin1String("dependent 'main.cpp' does not exist."), - Utils::FileName(), -1, + "dependent 'main.cpp' does not exist.", + FileName(), -1, Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << ""; QTest::newRow("Multiline error") - << QString::fromLatin1("c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE\\xutility(2227) : warning C4996: 'std::_Copy_impl': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'\n" - " c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE\\xutility(2212) : see declaration of 'std::_Copy_impl'\n" - " symbolgroupvalue.cpp(2314) : see reference to function template instantiation '_OutIt std::copy<const unsigned char*,unsigned short*>(_InIt,_InIt,_OutIt)' being compiled\n" - " with\n" - " [\n" - " _OutIt=unsigned short *,\n" - " _InIt=const unsigned char *\n" - " ]") + << "c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE\\xutility(2227) : warning C4996: 'std::_Copy_impl': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'\n" + " c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE\\xutility(2212) : see declaration of 'std::_Copy_impl'\n" + " symbolgroupvalue.cpp(2314) : see reference to function template instantiation '_OutIt std::copy<const unsigned char*,unsigned short*>(_InIt,_InIt,_OutIt)' being compiled\n" + " with\n" + " [\n" + " _OutIt=unsigned short *,\n" + " _InIt=const unsigned char *\n" + " ]" << OutputParserTester::STDOUT - << QString() << QString() + << "" << "" << (QList<Task>() << Task(Task::Warning, - QLatin1String("C4996: 'std::_Copy_impl': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'"), - Utils::FileName::fromUserInput(QLatin1String("c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE\\xutility")), 2227, + "C4996: 'std::_Copy_impl': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'", + FileName::fromUserInput("c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE\\xutility"), 2227, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Unknown, - QLatin1String("see declaration of 'std::_Copy_impl'"), - Utils::FileName::fromUserInput(QLatin1String("c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE\\xutility")), 2212, + "see declaration of 'std::_Copy_impl'", + FileName::fromUserInput("c:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\VC\\INCLUDE\\xutility"), 2212, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Unknown, - QLatin1String("see reference to function template instantiation '_OutIt std::copy<const unsigned char*,unsigned short*>(_InIt,_InIt,_OutIt)' being compiled\n" - "with\n" - "[\n" - " _OutIt=unsigned short *,\n" - " _InIt=const unsigned char *\n" - "]"), - Utils::FileName::fromUserInput(QLatin1String("symbolgroupvalue.cpp")), 2314, + "see reference to function template instantiation '_OutIt std::copy<const unsigned char*,unsigned short*>(_InIt,_InIt,_OutIt)' being compiled\n" + "with\n" + "[\n" + " _OutIt=unsigned short *,\n" + " _InIt=const unsigned char *\n" + "]", + FileName::fromUserInput("symbolgroupvalue.cpp"), 2314, Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << ""; QTest::newRow("Ambiguous symbol") - << QString::fromLatin1("D:\\Project\\file.h(98) : error C2872: 'UINT64' : ambiguous symbol\n" - " could be 'C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\include\\basetsd.h(83) : unsigned __int64 UINT64'\n" - " or 'D:\\Project\\types.h(71) : Types::UINT64'") + << "D:\\Project\\file.h(98) : error C2872: 'UINT64' : ambiguous symbol\n" + " could be 'C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\include\\basetsd.h(83) : unsigned __int64 UINT64'\n" + " or 'D:\\Project\\types.h(71) : Types::UINT64'" << OutputParserTester::STDOUT - << QString() << QString() + << "" << "" << (QList<Task>() << Task(Task::Error, - QLatin1String("C2872: 'UINT64' : ambiguous symbol"), - Utils::FileName::fromUserInput(QLatin1String("D:\\Project\\file.h")), 98, + "C2872: 'UINT64' : ambiguous symbol", + FileName::fromUserInput("D:\\Project\\file.h"), 98, + Constants::TASK_CATEGORY_COMPILE) + << Task(Task::Unknown, + "could be unsigned __int64 UINT64", + FileName::fromUserInput("C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\include\\basetsd.h"), 83, Constants::TASK_CATEGORY_COMPILE) - << Task(Task::Unknown, - QLatin1String("could be unsigned __int64 UINT64"), - Utils::FileName::fromUserInput(QLatin1String("C:\\Program Files (x86)\\Microsoft SDKs\\Windows\\v7.0A\\include\\basetsd.h")), 83, - Constants::TASK_CATEGORY_COMPILE) - << Task(Task::Unknown, - QLatin1String("or Types::UINT64"), - Utils::FileName::fromUserInput(QLatin1String("D:\\Project\\types.h")), 71, - Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << Task(Task::Unknown, + "or Types::UINT64", + FileName::fromUserInput("D:\\Project\\types.h"), 71, + Constants::TASK_CATEGORY_COMPILE)) + << ""; QTest::newRow("ignore moc note") - << QString::fromLatin1("/home/qtwebkithelpviewer.h:0: Note: No relevant classes found. No output generated.") + << "/home/qtwebkithelpviewer.h:0: Note: No relevant classes found. No output generated." << OutputParserTester::STDERR - << QString() << QString::fromLatin1("/home/qtwebkithelpviewer.h:0: Note: No relevant classes found. No output generated.\n") + << "" << "/home/qtwebkithelpviewer.h:0: Note: No relevant classes found. No output generated.\n" << (QList<ProjectExplorer::Task>()) - << QString(); + << ""; + + QTest::newRow("error with note") + << "main.cpp(7): error C2733: 'func': second C linkage of overloaded function not allowed\n" + "main.cpp(6): note: see declaration of 'func'" + << OutputParserTester::STDOUT + << "" << "" + << (QList<Task>() + << Task(Task::Error, + "C2733: 'func': second C linkage of overloaded function not allowed", + FileName::fromUserInput("main.cpp"), 7, + Constants::TASK_CATEGORY_COMPILE) + << Task(Task::Unknown, + "see declaration of 'func'", + FileName::fromUserInput("main.cpp"), 6, + Constants::TASK_CATEGORY_COMPILE)) + << ""; } void ProjectExplorerPlugin::testMsvcOutputParsers() @@ -574,68 +590,63 @@ void ProjectExplorerPlugin::testClangClOutputParsers_data() QTest::addColumn<QList<Task> >("tasks"); QTest::addColumn<QString>("outputLines"); - const QString warning1 = QLatin1String( -"private field 'm_version' is not used [-Wunused-private-field]\n" -"const int m_version; //! majorVersion<<8 + minorVersion"); - const QString warning2 = QLatin1String( -"unused variable 'formatTextPlainC' [-Wunused-const-variable]\n" -"static const char formatTextPlainC[] = \"text/plain\";"); - const QString warning3 = QLatin1String( -"unused variable 'formatTextHtmlC' [-Wunused-const-variable]\n" -"static const char formatTextHtmlC[] = \"text/html\";"); - const QString error1 = QLatin1String( -"unknown type name 'errr'\n" -" errr"); - const QString expectedError1 = QLatin1String( -"unknown type name 'errr'\n" -"errr"); // Line 2 trimmed. - const QString error2 = QLatin1String( -"expected unqualified-id\n" -"void *QWindowsGdiNativeInterface::nativeResourceForBackingStore(const QByteArray &resource, QBackingStore *bs)"); - - const QString clangClCompilerLog = QLatin1String( -"In file included from .\\qwindowseglcontext.cpp:40:\n" -"./qwindowseglcontext.h(282,15) : warning: ") + warning1 + QLatin1String("\n" -"5 warnings generated.\n" -".\\qwindowsclipboard.cpp(60,19) : warning: ") + warning2 + QLatin1String("\n" -" ^\n" -".\\qwindowsclipboard.cpp(61,19) : warning: ") + warning3 + QLatin1String("\n" -" ^\n" -"2 warnings generated.\n" -".\\qwindowsgdinativeinterface.cpp(48,3) : error: ") + error1 + QLatin1String("\n" -" ^\n" -".\\qwindowsgdinativeinterface.cpp(51,1) : error: ") + error2 + QLatin1String("\n" -"^\n" -"2 errors generated.\n"); - - const QString ignoredStderr = QLatin1String( -"NMAKE : fatal error U1077: 'D:\\opt\\LLVM64_390\\bin\\clang-cl.EXE' : return code '0x1'\n" -"Stop."); + const QString warning1 = "private field 'm_version' is not used [-Wunused-private-field]\n" + "const int m_version; //! majorVersion<<8 + minorVersion\n"; + const QString warning2 = "unused variable 'formatTextPlainC' [-Wunused-const-variable]\n" + "static const char formatTextPlainC[] = \"text/plain\";\n"; + const QString warning3 = "unused variable 'formatTextHtmlC' [-Wunused-const-variable]\n" + "static const char formatTextHtmlC[] = \"text/html\";\n"; + const QString error1 = "unknown type name 'errr'\n" + " errr\n"; + const QString expectedError1 = "unknown type name 'errr'\n" + "errr"; // Line 2 trimmed. + const QString error2 = + "expected unqualified-id\n" + "void *QWindowsGdiNativeInterface::nativeResourceForBackingStore(const QByteArray &resource, QBackingStore *bs)\n"; + + const QString clangClCompilerLog = + "In file included from .\\qwindowseglcontext.cpp:40:\n" + "./qwindowseglcontext.h(282,15) : warning: " + warning1 + + "5 warnings generated.\n" + ".\\qwindowsclipboard.cpp(60,19) : warning: " + warning2 + + " ^\n" + ".\\qwindowsclipboard.cpp(61,19) : warning: " + warning3 + + " ^\n" + "2 warnings generated.\n" + ".\\qwindowsgdinativeinterface.cpp(48,3) : error: " + error1 + + " ^\n" + ".\\qwindowsgdinativeinterface.cpp(51,1) : error: " + error2 + + "^\n" + "2 errors generated.\n"; + + const QString ignoredStderr = + "NMAKE : fatal error U1077: 'D:\\opt\\LLVM64_390\\bin\\clang-cl.EXE' : return code '0x1'\n" + "Stop."; const QString input = clangClCompilerLog + ignoredStderr; - const QString expectedStderr = ignoredStderr + QLatin1Char('\n'); + const QString expectedStderr = ignoredStderr + '\n'; QTest::newRow("error") << input << OutputParserTester::STDERR - << QString() << expectedStderr + << "" << expectedStderr << (QList<Task>() - << Task(Task::Warning, warning1, - Utils::FileName::fromUserInput(QLatin1String("./qwindowseglcontext.h")), 282, + << Task(Task::Warning, warning1.trimmed(), + FileName::fromUserInput("./qwindowseglcontext.h"), 282, Constants::TASK_CATEGORY_COMPILE) - << Task(Task::Warning, warning2, - Utils::FileName::fromUserInput(QLatin1String(".\\qwindowsclipboard.cpp")), 60, + << Task(Task::Warning, warning2.trimmed(), + FileName::fromUserInput(".\\qwindowsclipboard.cpp"), 60, Constants::TASK_CATEGORY_COMPILE) - << Task(Task::Warning, warning3, - Utils::FileName::fromUserInput(QLatin1String(".\\qwindowsclipboard.cpp")), 61, + << Task(Task::Warning, warning3.trimmed(), + FileName::fromUserInput(".\\qwindowsclipboard.cpp"), 61, Constants::TASK_CATEGORY_COMPILE) << Task(Task::Error, expectedError1, - Utils::FileName::fromUserInput(QLatin1String(".\\qwindowsgdinativeinterface.cpp")), 48, + FileName::fromUserInput(".\\qwindowsgdinativeinterface.cpp"), 48, Constants::TASK_CATEGORY_COMPILE) - << Task(Task::Error, error2, - Utils::FileName::fromUserInput(QLatin1String(".\\qwindowsgdinativeinterface.cpp")), 51, + << Task(Task::Error, error2.trimmed(), + FileName::fromUserInput(".\\qwindowsgdinativeinterface.cpp"), 51, Constants::TASK_CATEGORY_COMPILE)) - << QString(); + << ""; } void ProjectExplorerPlugin::testClangClOutputParsers() @@ -654,4 +665,6 @@ void ProjectExplorerPlugin::testClangClOutputParsers() outputLines); } +} // namespace ProjectExplorer + #endif // WITH_TEST diff --git a/src/plugins/projectexplorer/processstep.cpp b/src/plugins/projectexplorer/processstep.cpp index 9b95a0870c..0d5334c0e6 100644 --- a/src/plugins/projectexplorer/processstep.cpp +++ b/src/plugins/projectexplorer/processstep.cpp @@ -86,7 +86,7 @@ bool ProcessStep::init(QList<const BuildStep *> &earlierSteps) void ProcessStep::run(QFutureInterface<bool> & fi) { - return AbstractProcessStep::run(fi); + AbstractProcessStep::run(fi); } BuildStepConfigWidget *ProcessStep::createConfigWidget() diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp index 578cc033ec..bb62390fe4 100644 --- a/src/plugins/projectexplorer/project.cpp +++ b/src/plugins/projectexplorer/project.cpp @@ -141,6 +141,8 @@ public: QVariantMap m_pluginSettings; Internal::UserFileAccessor *m_accessor = nullptr; + QString m_displayName; + Kit::Predicate m_requiredKitPredicate; Kit::Predicate m_preferredKitPredicate; @@ -178,6 +180,11 @@ Project::~Project() delete d; } +QString Project::displayName() const +{ + return d->m_displayName; +} + Core::Id Project::id() const { QTC_CHECK(d->m_id.isValid()); @@ -455,6 +462,14 @@ bool Project::setupTarget(Target *t) return true; } +void Project::setDisplayName(const QString &name) +{ + if (name == d->m_displayName) + return; + d->m_displayName = name; + emit displayNameChanged(); +} + void Project::setId(Core::Id id) { d->m_id = id; @@ -477,10 +492,13 @@ void Project::setRootProjectNode(ProjectNode *root) ProjectNode *oldNode = d->m_rootProjectNode; d->m_rootProjectNode = root; - if (root) + if (root) { root->setParentFolderNode(d->m_containerNode); - ProjectTree::emitSubtreeChanged(root); - emit fileListChanged(); + // Only announce non-null root, null is only used when project is destroyed. + // In that case SessionManager::projectRemoved() triggers the update. + ProjectTree::emitSubtreeChanged(root); + emit fileListChanged(); + } delete oldNode; } diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h index 343643b281..edc5b4c2ce 100644 --- a/src/plugins/projectexplorer/project.h +++ b/src/plugins/projectexplorer/project.h @@ -92,7 +92,7 @@ public: const ProjectDocument::ProjectCallback &callback = {}); ~Project() override; - virtual QString displayName() const = 0; + QString displayName() const; Core::Id id() const; Core::IDocument *document() const; @@ -164,6 +164,7 @@ public: Utils::MacroExpander *macroExpander() const; signals: + void displayNameChanged(); void fileListChanged(); // Note: activeTarget can be 0 (if no targets are defined). @@ -190,6 +191,7 @@ protected: virtual RestoreResult fromMap(const QVariantMap &map, QString *errorMessage); virtual bool setupTarget(Target *t); + void setDisplayName(const QString &name); void setRequiredKitPredicate(const Kit::Predicate &predicate); void setPreferredKitPredicate(const Kit::Predicate &predicate); diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 20f4d27ea1..c18c65a13c 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -2927,8 +2927,6 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions() Node *currentNode = ProjectTree::currentNode(); if (currentNode && currentNode->managingProject()) { - QList<ProjectAction> actions = currentNode->supportedActions(currentNode); - ProjectNode *pn; if (ContainerNode *cn = currentNode->asContainerNode()) pn = cn->rootProjectNode(); @@ -2956,56 +2954,61 @@ void ProjectExplorerPluginPrivate::updateContextMenuActions() } } } + + auto supports = [currentNode](ProjectAction action) { + return currentNode->supportsAction(action, currentNode); + }; + if (currentNode->asFolderNode()) { // Also handles ProjectNode - m_addNewFileAction->setEnabled(actions.contains(AddNewFile) + m_addNewFileAction->setEnabled(currentNode->supportsAction(AddNewFile, currentNode) && !ICore::isNewItemDialogRunning()); m_addNewSubprojectAction->setEnabled(currentNode->nodeType() == NodeType::Project - && actions.contains(AddSubProject) + && supports(AddSubProject) && !ICore::isNewItemDialogRunning()); m_removeProjectAction->setEnabled(currentNode->nodeType() == NodeType::Project - && actions.contains(RemoveSubProject)); - m_addExistingFilesAction->setEnabled(actions.contains(AddExistingFile)); - m_addExistingDirectoryAction->setEnabled(actions.contains(AddExistingDirectory)); - m_renameFileAction->setEnabled(actions.contains(Rename)); + && supports(RemoveSubProject)); + m_addExistingFilesAction->setEnabled(supports(AddExistingFile)); + m_addExistingDirectoryAction->setEnabled(supports(AddExistingDirectory)); + m_renameFileAction->setEnabled(supports(Rename)); } else if (currentNode->asFileNode()) { // Enable and show remove / delete in magic ways: // If both are disabled show Remove // If both are enabled show both (can't happen atm) // If only removeFile is enabled only show it // If only deleteFile is enable only show it - bool enableRemove = actions.contains(RemoveFile); + bool enableRemove = supports(RemoveFile); m_removeFileAction->setEnabled(enableRemove); - bool enableDelete = actions.contains(EraseFile); + bool enableDelete = supports(EraseFile); m_deleteFileAction->setEnabled(enableDelete); m_deleteFileAction->setVisible(enableDelete); m_removeFileAction->setVisible(!enableDelete || enableRemove); - m_renameFileAction->setEnabled(actions.contains(Rename)); + m_renameFileAction->setEnabled(supports(Rename)); const bool currentNodeIsTextFile = isTextFile( ProjectTree::currentNode()->filePath().toString()); m_diffFileAction->setEnabled(isDiffServiceAvailable() && currentNodeIsTextFile && TextEditor::TextDocument::currentTextDocument()); - m_duplicateFileAction->setVisible(actions.contains(DuplicateFile)); - m_duplicateFileAction->setEnabled(actions.contains(DuplicateFile)); + m_duplicateFileAction->setVisible(supports(DuplicateFile)); + m_duplicateFileAction->setEnabled(supports(DuplicateFile)); EditorManager::populateOpenWithMenu(m_openWithMenu, ProjectTree::currentNode()->filePath().toString()); } - if (actions.contains(HidePathActions)) { + if (supports(HidePathActions)) { m_openTerminalHere->setVisible(false); m_showInGraphicalShell->setVisible(false); m_searchOnFileSystem->setVisible(false); } - if (actions.contains(HideFileActions)) { + if (supports(HideFileActions)) { m_deleteFileAction->setVisible(false); m_removeFileAction->setVisible(false); } - if (actions.contains(HideFolderActions)) { + if (supports(HideFolderActions)) { m_addNewFileAction->setVisible(false); m_addNewSubprojectAction->setVisible(false); m_removeProjectAction->setVisible(false); @@ -3041,8 +3044,7 @@ void ProjectExplorerPluginPrivate::addNewSubproject() QString location = directoryFor(currentNode); if (currentNode->nodeType() == NodeType::Project - && currentNode->supportedActions( - currentNode).contains(AddSubProject)) { + && currentNode->supportsAction(AddSubProject, currentNode)) { QVariantMap map; map.insert(QLatin1String(Constants::PREFERRED_PROJECT_NODE), QVariant::fromValue(currentNode)); Project *project = ProjectTree::currentProject(); @@ -3163,29 +3165,33 @@ void ProjectExplorerPluginPrivate::removeFile() Node *currentNode = ProjectTree::currentNode(); QTC_ASSERT(currentNode && currentNode->nodeType() == NodeType::File, return); - FileNode *fileNode = currentNode->asFileNode(); - - QString filePath = currentNode->filePath().toString(); - RemoveFileDialog removeFileDialog(filePath, ICore::mainWindow()); + const Utils::FileName filePath = currentNode->filePath(); + RemoveFileDialog removeFileDialog(filePath.toString(), ICore::mainWindow()); if (removeFileDialog.exec() == QDialog::Accepted) { const bool deleteFile = removeFileDialog.isDeleteFileChecked(); + // Re-read the current node, in case the project is re-parsed while the dialog is open + if (currentNode != ProjectTree::currentNode()) { + currentNode = ProjectTreeWidget::nodeForFile(filePath); + QTC_ASSERT(currentNode && currentNode->nodeType() == NodeType::File, return); + } + // remove from project - FolderNode *folderNode = fileNode->parentFolderNode(); - Q_ASSERT(folderNode); + FolderNode *folderNode = currentNode->asFileNode()->parentFolderNode(); + QTC_ASSERT(folderNode, return); - if (!folderNode->removeFiles(QStringList(filePath))) { + if (!folderNode->removeFiles(QStringList(filePath.toString()))) { QMessageBox::warning(ICore::mainWindow(), tr("Removing File Failed"), tr("Could not remove file %1 from project %2.") - .arg(QDir::toNativeSeparators(filePath)) + .arg(filePath.toUserOutput()) .arg(folderNode->managingProject()->displayName())); if (!deleteFile) return; } - FileChangeBlocker changeGuard(filePath); - FileUtils::removeFile(filePath, deleteFile); + FileChangeBlocker changeGuard(filePath.toString()); + FileUtils::removeFile(filePath.toString(), deleteFile); } } @@ -3212,7 +3218,7 @@ void ProjectExplorerPluginPrivate::duplicateFile() // Create a copy and add the file to the parent folder node. FolderNode *folderNode = fileNode->parentFolderNode(); - Q_ASSERT(folderNode); + QTC_ASSERT(folderNode, return); if (!(QFile::copy(filePath, newFilePath) && folderNode->addFiles(QStringList(newFilePath)))) { QMessageBox::warning(ICore::mainWindow(), tr("Duplicating File Failed"), tr("Could not duplicate the file %1.") diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp index d9b7a80d41..9a5bb11407 100644 --- a/src/plugins/projectexplorer/projectmodels.cpp +++ b/src/plugins/projectexplorer/projectmodels.cpp @@ -74,15 +74,14 @@ FlatModel::FlatModel(QObject *parent) : TreeModel<WrapperNode, WrapperNode>(new WrapperNode(nullptr), parent) { ProjectTree *tree = ProjectTree::instance(); - connect(tree, &ProjectTree::subtreeChanged, this, &FlatModel::update); + connect(tree, &ProjectTree::subtreeChanged, this, &FlatModel::updateSubtree); SessionManager *sm = SessionManager::instance(); - connect(sm, &SessionManager::projectRemoved, this, &FlatModel::update); - connect(sm, &SessionManager::sessionLoaded, this, &FlatModel::loadExpandData); + connect(sm, &SessionManager::projectRemoved, this, &FlatModel::handleProjectRemoved); + connect(sm, &SessionManager::aboutToLoadSession, this, &FlatModel::loadExpandData); connect(sm, &SessionManager::aboutToSaveSession, this, &FlatModel::saveExpandData); connect(sm, &SessionManager::projectAdded, this, &FlatModel::handleProjectAdded); connect(sm, &SessionManager::startupProjectChanged, this, [this] { layoutChanged(); }); - update(); } QVariant FlatModel::data(const QModelIndex &index, int role) const @@ -145,7 +144,7 @@ Qt::ItemFlags FlatModel::flags(const QModelIndex &index) const if (Node *node = nodeForIndex(index)) { if (!node->asProjectNode()) { // either folder or file node - if (node->supportedActions(node).contains(Rename)) + if (node->supportsAction(Rename, node)) f = f | Qt::ItemIsEditable; } } @@ -170,42 +169,27 @@ bool FlatModel::setData(const QModelIndex &index, const QVariant &value, int rol return true; } -void FlatModel::update() +void FlatModel::addOrRebuildProjectModel(Project *project) { - rebuildModel(); -} - -void FlatModel::rebuildModel() -{ - QList<Project *> projects = SessionManager::projects(); - - Utils::sort(projects, [](Project *p1, Project *p2) { - const int displayNameResult = caseFriendlyCompare(p1->displayName(), p2->displayName()); - if (displayNameResult != 0) - return displayNameResult < 0; - return p1 < p2; // sort by pointer value - }); + WrapperNode *container = nodeForProject(project); + if (container) { + container->removeChildren(); + } else { + container = new WrapperNode(project->containerNode()); + rootItem()->appendChild(container); + } QSet<Node *> seen; - rootItem()->removeChildren(); - for (Project *project : projects) { - WrapperNode *container = new WrapperNode(project->containerNode()); - - ProjectNode *projectNode = project->rootProjectNode(); - if (projectNode) { - addFolderNode(container, projectNode, &seen); - } else { - FileNode *projectFileNode = new FileNode(project->projectFilePath(), FileType::Project, false); - seen.insert(projectFileNode); - container->appendChild(new WrapperNode(projectFileNode)); - } - - container->sortChildren(&sortWrapperNodes); - rootItem()->appendChild(container); + if (ProjectNode *projectNode = project->rootProjectNode()) { + addFolderNode(container, projectNode, &seen); + } else { + FileNode *projectFileNode = new FileNode(project->projectFilePath(), FileType::Project, false); + seen.insert(projectFileNode); + container->appendChild(new WrapperNode(projectFileNode)); } - forAllItems([this](WrapperNode *node) { + container->forAllChildren([this](WrapperNode *node) { if (node->m_node) { const QString path = node->m_node->filePath().toString(); const QString displayName = node->m_node->displayName(); @@ -216,6 +200,37 @@ void FlatModel::rebuildModel() emit requestExpansion(node->index()); } }); + + const QString path = container->m_node->filePath().toString(); + const QString displayName = container->m_node->displayName(); + ExpandData ed(path, displayName); + if (m_toExpand.contains(ed)) + emit requestExpansion(container->index()); +} + +void FlatModel::updateSubtree(FolderNode *node) +{ + // FIXME: This is still excessive, should be limited to the affected subtree. + while (FolderNode *parent = node->parentFolderNode()) + node = parent; + if (ContainerNode *container = node->asContainerNode()) + addOrRebuildProjectModel(container->project()); +} + +void FlatModel::rebuildModel() +{ + QList<Project *> projects = SessionManager::projects(); + QTC_CHECK(projects.size() == rootItem()->childCount()); + + Utils::sort(projects, [](Project *p1, Project *p2) { + const int displayNameResult = caseFriendlyCompare(p1->displayName(), p2->displayName()); + if (displayNameResult != 0) + return displayNameResult < 0; + return p1 < p2; // sort by pointer value + }); + + for (Project *project : projects) + addOrRebuildProjectModel(project); } void FlatModel::onCollapsed(const QModelIndex &idx) @@ -238,9 +253,22 @@ ExpandData FlatModel::expandDataForNode(const Node *node) const void FlatModel::handleProjectAdded(Project *project) { - Node *node = project->containerNode(); - m_toExpand.insert(expandDataForNode(node)); - update(); + addOrRebuildProjectModel(project); +} + +void FlatModel::handleProjectRemoved(Project *project) +{ + destroyItem(nodeForProject(project)); +} + +WrapperNode *FlatModel::nodeForProject(Project *project) +{ + QTC_ASSERT(project, return nullptr); + ContainerNode *containerNode = project->containerNode(); + QTC_ASSERT(containerNode, return nullptr); + return rootItem()->findFirstLevelChild([containerNode](WrapperNode *node) { + return node->m_node == containerNode; + }); } void FlatModel::loadExpandData() diff --git a/src/plugins/projectexplorer/projectmodels.h b/src/plugins/projectexplorer/projectmodels.h index b59e08e04f..18129a7ddc 100644 --- a/src/plugins/projectexplorer/projectmodels.h +++ b/src/plugins/projectexplorer/projectmodels.h @@ -90,7 +90,7 @@ private: static const QLoggingCategory &logger(); - void update(); + void updateSubtree(FolderNode *node); void rebuildModel(); void addFolderNode(WrapperNode *parent, FolderNode *folderNode, QSet<Node *> *seen); @@ -98,6 +98,9 @@ private: void loadExpandData(); void saveExpandData(); void handleProjectAdded(Project *project); + void handleProjectRemoved(Project *project); + WrapperNode *nodeForProject(Project *project); + void addOrRebuildProjectModel(Project *project); QTimer m_timer; QSet<ExpandData> m_toExpand; diff --git a/src/plugins/projectexplorer/projectnodes.cpp b/src/plugins/projectexplorer/projectnodes.cpp index 45c78f72ca..8a04235ea6 100644 --- a/src/plugins/projectexplorer/projectnodes.cpp +++ b/src/plugins/projectexplorer/projectnodes.cpp @@ -215,14 +215,9 @@ bool Node::isEnabled() const return parent ? parent->isEnabled() : true; } -QList<ProjectAction> Node::supportedActions(Node *node) const +bool Node::supportsAction(ProjectAction, Node *) const { - if (FolderNode *folder = parentFolderNode()) { - QList<ProjectAction> list = folder->supportedActions(node); - list.append(InheritedFromParent); - return list; - } - return {}; + return false; } void Node::setEnabled(bool enabled) @@ -364,6 +359,14 @@ QList<FileNode *> FileNode::scanForFiles(const Utils::FileName &directory, return scanForFilesRecursively(directory, factory, visited, future, 0.0, 1000000.0); } +bool FileNode::supportsAction(ProjectAction action, Node *node) const +{ + if (action == InheritedFromParent) + return true; + FolderNode *parentFolder = parentFolderNode(); + return parentFolder && parentFolder->supportsAction(action, node); +} + /*! \class ProjectExplorer::FolderNode @@ -593,6 +596,14 @@ QString FolderNode::addFileFilter() const return parentFolderNode()->addFileFilter(); } +bool FolderNode::supportsAction(ProjectAction action, Node *node) const +{ + if (action == InheritedFromParent) + return true; + FolderNode *parentFolder = parentFolderNode(); + return parentFolder && parentFolder->supportsAction(action, node); +} + bool FolderNode::addFiles(const QStringList &filePaths, QStringList *notAdded) { ProjectNode *pn = managingProject(); @@ -762,6 +773,11 @@ bool ProjectNode::renameFile(const QString &filePath, const QString &newFilePath return false; } +bool ProjectNode::supportsAction(ProjectAction, Node *) const +{ + return false; +} + bool ProjectNode::deploysFolder(const QString &folder) const { Q_UNUSED(folder); @@ -812,11 +828,10 @@ QString ContainerNode::displayName() const return name; } -QList<ProjectAction> ContainerNode::supportedActions(Node *node) const +bool ContainerNode::supportsAction(ProjectAction action, Node *node) const { - if (Node *rootNode = m_project->rootProjectNode()) - return rootNode->supportedActions(node); - return {}; + Node *rootNode = m_project->rootProjectNode(); + return rootNode && rootNode->supportsAction(action, node); } ProjectNode *ContainerNode::rootProjectNode() const diff --git a/src/plugins/projectexplorer/projectnodes.h b/src/plugins/projectexplorer/projectnodes.h index 4ed5b4eb56..d82c02f53c 100644 --- a/src/plugins/projectexplorer/projectnodes.h +++ b/src/plugins/projectexplorer/projectnodes.h @@ -130,7 +130,7 @@ public: virtual QString tooltip() const; bool isEnabled() const; - virtual QList<ProjectAction> supportedActions(Node *node) const; + virtual bool supportsAction(ProjectAction action, Node *node) const; void setEnabled(bool enabled); void setAbsoluteFilePathAndLine(const Utils::FileName &filePath, int line); @@ -179,6 +179,7 @@ public: static QList<FileNode *> scanForFiles(const Utils::FileName &directory, const std::function<FileNode *(const Utils::FileName &fileName)> factory, QFutureInterface<QList<FileNode *>> *future = nullptr); + bool supportsAction(ProjectAction action, Node *node) const override; private: FileType m_fileType; @@ -225,6 +226,8 @@ public: virtual QString addFileFilter() const; + bool supportsAction(ProjectAction action, Node *node) const override; + virtual bool addFiles(const QStringList &filePaths, QStringList *notAdded = 0); virtual bool removeFiles(const QStringList &filePaths, QStringList *notRemoved = 0); virtual bool deleteFiles(const QStringList &filePaths); @@ -288,6 +291,7 @@ public: bool deleteFiles(const QStringList &filePaths) override; bool canRenameFile(const QString &filePath, const QString &newFilePath) override; bool renameFile(const QString &filePath, const QString &newFilePath) override; + bool supportsAction(ProjectAction action, Node *node) const override; // by default returns false virtual bool deploysFolder(const QString &folder) const; @@ -309,12 +313,13 @@ public: ContainerNode(Project *project); QString displayName() const final; - QList<ProjectAction> supportedActions(Node *node) const final; + bool supportsAction(ProjectAction action, Node *node) const final; ContainerNode *asContainerNode() final { return this; } const ContainerNode *asContainerNode() const final { return this; } ProjectNode *rootProjectNode() const; + Project *project() const { return m_project; } private: Project *m_project; diff --git a/src/plugins/projectexplorer/projectwelcomepage.cpp b/src/plugins/projectexplorer/projectwelcomepage.cpp index f7e442ed5e..9518891ece 100644 --- a/src/plugins/projectexplorer/projectwelcomepage.cpp +++ b/src/plugins/projectexplorer/projectwelcomepage.cpp @@ -56,6 +56,7 @@ using namespace Core; using namespace Utils; const int LINK_HEIGHT = 35; +const int SESSION_LINE_HEIGHT = 30; const char PROJECT_BASE_ID[] = "Welcome.OpenRecentProject"; namespace ProjectExplorer { @@ -292,7 +293,7 @@ public: if (isActiveSession && !isDefaultVirgin) fullSessionName = ProjectWelcomePage::tr("%1 (current session)").arg(fullSessionName); - const QRect switchRect = QRect(x, y, rc.width() - 24, firstBase + 3 - y); + const QRect switchRect = QRect(x, y, rc.width() - 24, SESSION_LINE_HEIGHT); const bool switchActive = switchRect.contains(mousePos); painter->setPen(linkColor); painter->setFont(sizedFont(13, option.widget, switchActive)); @@ -350,7 +351,7 @@ public: QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &idx) const final { - int h = 30; + int h = SESSION_LINE_HEIGHT; QString sessionName = idx.data(Qt::DisplayRole).toString(); if (m_expandedSessions.contains(sessionName)) { QStringList projects = SessionManager::projectsForSessionName(sessionName); @@ -364,7 +365,7 @@ public: { if (ev->type() == QEvent::MouseButtonRelease) { const QPoint pos = static_cast<QMouseEvent *>(ev)->pos(); - const QRect rc(option.rect.right() - 24, option.rect.top(), 24, 30); + const QRect rc(option.rect.right() - 24, option.rect.top(), 24, SESSION_LINE_HEIGHT); const QString sessionName = idx.data(Qt::DisplayRole).toString(); if (rc.contains(pos)) { // The expand/collapse "button". diff --git a/src/plugins/projectexplorer/projectwindow.cpp b/src/plugins/projectexplorer/projectwindow.cpp index b3ae4b85f9..88eac526e0 100644 --- a/src/plugins/projectexplorer/projectwindow.cpp +++ b/src/plugins/projectexplorer/projectwindow.cpp @@ -544,7 +544,7 @@ public: QString dir = project->projectDirectory().toString(); QString importDir = QFileDialog::getExistingDirectory(ICore::mainWindow(), - ProjectWindow::tr("Import directory"), + ProjectWindow::tr("Import Directory"), dir); FileName path = FileName::fromString(importDir); diff --git a/src/plugins/projectexplorer/projectwizardpage.cpp b/src/plugins/projectexplorer/projectwizardpage.cpp index 88756a3599..951f2b10fd 100644 --- a/src/plugins/projectexplorer/projectwizardpage.cpp +++ b/src/plugins/projectexplorer/projectwizardpage.cpp @@ -240,8 +240,7 @@ static inline AddNewTree *buildAddProjectTree(ProjectNode *root, const QString & } } - const QList<ProjectAction> &list = root->supportedActions(root); - if (list.contains(AddSubProject) && !list.contains(InheritedFromParent)) { + if (root->supportsAction(AddSubProject, root) && !root->supportsAction(InheritedFromParent, root)) { if (projectPath.isEmpty() || root->canAddSubProject(projectPath)) { FolderNode::AddNewInformation info = root->addNewInformation(QStringList() << projectPath, contextNode); auto item = new AddNewTree(root, children, info); @@ -265,8 +264,7 @@ static inline AddNewTree *buildAddFilesTree(FolderNode *root, const QStringList children.append(child); } - const QList<ProjectAction> &list = root->supportedActions(root); - if (list.contains(AddNewFile) && !list.contains(InheritedFromParent)) { + if (root->supportsAction(AddNewFile, root) && !root->supportsAction(InheritedFromParent, root)) { FolderNode::AddNewInformation info = root->addNewInformation(files, contextNode); auto item = new AddNewTree(root, children, info); selector->inspect(item, root == contextNode); diff --git a/src/plugins/projectexplorer/session.cpp b/src/plugins/projectexplorer/session.cpp index 9286155bfb..5b8e6d382c 100644 --- a/src/plugins/projectexplorer/session.cpp +++ b/src/plugins/projectexplorer/session.cpp @@ -382,6 +382,8 @@ void SessionManager::addProject(Project *pro) d->m_projects.append(pro); connect(pro, &Project::fileListChanged, m_instance, &SessionManager::clearProjectFileCache); + connect(pro, &Project::displayNameChanged, + m_instance, [pro]() { m_instance->projectDisplayNameChanged(pro); }); emit m_instance->projectAdded(pro); configureEditors(pro); diff --git a/src/plugins/projectexplorer/sessiondialog.cpp b/src/plugins/projectexplorer/sessiondialog.cpp index 53be179640..d3eba933c5 100644 --- a/src/plugins/projectexplorer/sessiondialog.cpp +++ b/src/plugins/projectexplorer/sessiondialog.cpp @@ -121,6 +121,7 @@ bool SessionNameInputDialog::isSwitchToRequested() const SessionDialog::SessionDialog(QWidget *parent) : QDialog(parent) { m_ui.setupUi(this); + m_ui.sessionView->setActivationMode(Utils::DoubleClickActivation); connect(m_ui.btCreateNew, &QAbstractButton::clicked, m_ui.sessionView, &SessionView::createNewSession); diff --git a/src/plugins/projectexplorer/sessionview.cpp b/src/plugins/projectexplorer/sessionview.cpp index f08438d07b..374f6e0745 100644 --- a/src/plugins/projectexplorer/sessionview.cpp +++ b/src/plugins/projectexplorer/sessionview.cpp @@ -53,7 +53,7 @@ void RemoveItemFocusDelegate::paint(QPainter* painter, const QStyleOptionViewIte } SessionView::SessionView(QWidget *parent) - : QTreeView(parent) + : Utils::TreeView(parent) { setItemDelegate(new RemoveItemFocusDelegate(this)); setSelectionBehavior(QAbstractItemView::SelectRows); @@ -68,7 +68,7 @@ SessionView::SessionView(QWidget *parent) selectionModel()->select(firstRow, QItemSelectionModel::QItemSelectionModel:: SelectCurrent); - connect(this, &QTreeView::activated, [this](const QModelIndex &index){ + connect(this, &Utils::TreeView::activated, [this](const QModelIndex &index){ emit activated(m_sessionModel.sessionAt(index.row())); }); connect(selectionModel(), &QItemSelectionModel::currentRowChanged, [this] @@ -133,7 +133,7 @@ void SessionView::selectSession(const QString &sessionName) void SessionView::showEvent(QShowEvent *event) { - QTreeView::showEvent(event); + Utils::TreeView::showEvent(event); selectActiveSession(); setFocus(); } diff --git a/src/plugins/projectexplorer/sessionview.h b/src/plugins/projectexplorer/sessionview.h index b43f703045..0f85e6ed21 100644 --- a/src/plugins/projectexplorer/sessionview.h +++ b/src/plugins/projectexplorer/sessionview.h @@ -27,13 +27,14 @@ #include "sessionmodel.h" +#include <utils/itemviews.h> + #include <QAbstractTableModel> -#include <QTreeView> namespace ProjectExplorer { namespace Internal { -class SessionView : public QTreeView { +class SessionView : public Utils::TreeView { Q_OBJECT public: diff --git a/src/plugins/projectexplorer/targetsettingspanel.cpp b/src/plugins/projectexplorer/targetsettingspanel.cpp index 65780a6d35..de40db6c66 100644 --- a/src/plugins/projectexplorer/targetsettingspanel.cpp +++ b/src/plugins/projectexplorer/targetsettingspanel.cpp @@ -442,7 +442,7 @@ public: m_project->removeTarget(t); }); - QMenu *copyMenu = menu->addMenu(tr("Copy Steps From Other Kit...")); + QMenu *copyMenu = menu->addMenu(tr("Copy Steps From Another Kit...")); if (m_kitId.isValid() && m_project->target(m_kitId)) { const QList<Kit *> kits = KitManager::kits(); for (Kit *kit : kits) { diff --git a/src/plugins/pythoneditor/pythoneditorplugin.cpp b/src/plugins/pythoneditor/pythoneditorplugin.cpp index 1e97696fb3..265212d39d 100644 --- a/src/plugins/pythoneditor/pythoneditorplugin.cpp +++ b/src/plugins/pythoneditor/pythoneditorplugin.cpp @@ -94,8 +94,6 @@ class PythonProject : public Project public: explicit PythonProject(const Utils::FileName &filename); - QString displayName() const override; - bool addFiles(const QStringList &filePaths); bool removeFiles(const QStringList &filePaths); bool setFiles(const QStringList &filePaths); @@ -122,11 +120,7 @@ public: PythonProjectNode(PythonProject *project); bool showInSimpleTree() const override; - - QList<ProjectAction> supportedActions(Node *node) const override; - QString addFileFilter() const override; - bool renameFile(const QString &filePath, const QString &newFilePath) override; private: @@ -381,11 +375,7 @@ PythonProject::PythonProject(const FileName &fileName) : setId(PythonProjectId); setProjectContext(Context(PythonProjectContext)); setProjectLanguages(Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); -} - -QString PythonProject::displayName() const -{ - return projectFilePath().toFileInfo().completeBaseName(); + setDisplayName(fileName.toFileInfo().completeBaseName()); } static QStringList readLines(const QString &absoluteFileName) @@ -655,13 +645,6 @@ bool PythonProjectNode::showInSimpleTree() const return true; } -QList<ProjectAction> PythonProjectNode::supportedActions(Node *node) const -{ - Q_UNUSED(node); - //return { AddNewFile, AddExistingFile, AddExistingDirectory, RemoveFile, Rename }; - return {}; -} - QString PythonProjectNode::addFileFilter() const { return QLatin1String("*.py"); diff --git a/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp b/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp index f5a8ddb46d..84905e8f17 100644 --- a/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp +++ b/src/plugins/qbsprojectmanager/defaultpropertyprovider.cpp @@ -40,7 +40,6 @@ #include <qbs.h> #include <ios/iosconstants.h> -#include <qnx/qnxconstants.h> #include <winrt/winrtconstants.h> #include <QDir> @@ -54,7 +53,6 @@ using namespace Constants; namespace Internal { using namespace ProjectExplorer::Constants; using namespace Ios::Constants; -using namespace Qnx::Constants; using namespace WinRt::Internal::Constants; static QString extractToolchainPrefix(QString *compilerName) @@ -119,10 +117,11 @@ static QStringList targetOSList(const ProjectExplorer::Abi &abi, const ProjectEx } os << QLatin1String("bsd") << QLatin1String("unix"); break; + case ProjectExplorer::Abi::QnxOS: + os << QLatin1String("qnx") << QLatin1String("unix"); + break; case ProjectExplorer::Abi::UnixOS: - if (device == QNX_QNX_OS_TYPE) - os << QLatin1String("qnx"); - else if (abi.osFlavor() == ProjectExplorer::Abi::SolarisUnixFlavor) + if (abi.osFlavor() == ProjectExplorer::Abi::SolarisUnixFlavor) os << QLatin1String("solaris"); os << QLatin1String("unix"); break; diff --git a/src/plugins/qbsprojectmanager/qbsnodes.cpp b/src/plugins/qbsprojectmanager/qbsnodes.cpp index 730c63ef7f..262145ec6d 100644 --- a/src/plugins/qbsprojectmanager/qbsnodes.cpp +++ b/src/plugins/qbsprojectmanager/qbsnodes.cpp @@ -45,6 +45,8 @@ #include <QIcon> #include <QStyle> +using namespace ProjectExplorer; + // ---------------------------------------------------------------------- // Helpers: // ---------------------------------------------------------------------- @@ -223,20 +225,23 @@ public: }; -static QList<ProjectExplorer::ProjectAction> supportedNodeActions(ProjectExplorer::Node *node, - bool managesFiles) +static bool supportsNodeAction(ProjectAction action, Node *node) { - QList<ProjectExplorer::ProjectAction> actions; const QbsProject * const project = parentQbsProjectNode(node)->project(); if (!project->isProjectEditable()) - return actions; - if (managesFiles) - actions << ProjectExplorer::AddNewFile << ProjectExplorer::AddExistingFile; - if (node->nodeType() == ProjectExplorer::NodeType::File - && !project->qbsProject().buildSystemFiles().contains(node->filePath().toString())) { - actions << ProjectExplorer::RemoveFile << ProjectExplorer::Rename; + return false; + + auto equalsNodeFilePath = [node](const QString &str) + { + return str == node->filePath().toString(); + }; + + if (action == RemoveFile || action == Rename) { + if (node->nodeType() == ProjectExplorer::NodeType::File) + return !Utils::contains(project->qbsProject().buildSystemFiles(), equalsNodeFilePath); } - return actions; + + return false; } // ---------------------------------------------------------------------- @@ -265,9 +270,9 @@ QbsFolderNode::QbsFolderNode(const Utils::FileName &folderPath, ProjectExplorer: { } -QList<ProjectExplorer::ProjectAction> QbsFolderNode::supportedActions(ProjectExplorer::Node *node) const +bool QbsFolderNode::supportsAction(ProjectAction action, Node *node) const { - return supportedNodeActions(node, false); + return supportsNodeAction(action, node); } // --------------------------------------------------------------------------- @@ -283,12 +288,6 @@ bool QbsBaseProjectNode::showInSimpleTree() const return false; } -QList<ProjectExplorer::ProjectAction> QbsBaseProjectNode::supportedActions(ProjectExplorer::Node *node) const -{ - Q_UNUSED(node); - return QList<ProjectExplorer::ProjectAction>(); -} - // -------------------------------------------------------------------- // QbsGroupNode: // -------------------------------------------------------------------- @@ -303,9 +302,12 @@ QbsGroupNode::QbsGroupNode(const qbs::GroupData &grp, const QString &productPath m_qbsGroupData = grp; } -QList<ProjectExplorer::ProjectAction> QbsGroupNode::supportedActions(ProjectExplorer::Node *node) const +bool QbsGroupNode::supportsAction(ProjectAction action, Node *node) const { - return supportedNodeActions(node, true); + if (action == AddNewFile || action == AddExistingFile) + return true; + + return supportsNodeAction(action, node); } bool QbsGroupNode::addFiles(const QStringList &filePaths, QStringList *notAdded) @@ -382,9 +384,12 @@ bool QbsProductNode::showInSimpleTree() const return true; } -QList<ProjectExplorer::ProjectAction> QbsProductNode::supportedActions(ProjectExplorer::Node *node) const +bool QbsProductNode::supportsAction(ProjectAction action, Node *node) const { - return supportedNodeActions(node, true); + if (action == AddNewFile || action == AddExistingFile) + return true; + + return supportsNodeAction(action, node); } bool QbsProductNode::addFiles(const QStringList &filePaths, QStringList *notAdded) diff --git a/src/plugins/qbsprojectmanager/qbsnodes.h b/src/plugins/qbsprojectmanager/qbsnodes.h index 313fa2a6ff..904851bc81 100644 --- a/src/plugins/qbsprojectmanager/qbsnodes.h +++ b/src/plugins/qbsprojectmanager/qbsnodes.h @@ -55,7 +55,7 @@ public: const QString &displayName); private: - QList<ProjectExplorer::ProjectAction> supportedActions(ProjectExplorer::Node *node) const override; + bool supportsAction(ProjectExplorer::ProjectAction action, Node *node) const final; }; // --------------------------------------------------------------------------- @@ -70,10 +70,6 @@ public: explicit QbsBaseProjectNode(const Utils::FileName &absoluteFilePath); bool showInSimpleTree() const override; - - QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const override; -private: - friend class QbsGroupNode; }; // -------------------------------------------------------------------- @@ -85,7 +81,7 @@ class QbsGroupNode : public QbsBaseProjectNode public: QbsGroupNode(const qbs::GroupData &grp, const QString &productPath); - QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const override; + bool supportsAction(ProjectExplorer::ProjectAction action, Node *node) const final; bool addFiles(const QStringList &filePaths, QStringList *notAdded = 0) override; bool removeFiles(const QStringList &filePaths, QStringList *notRemoved = 0) override; bool renameFile(const QString &filePath, const QString &newFilePath) override; @@ -107,7 +103,7 @@ public: explicit QbsProductNode(const qbs::ProductData &prd); bool showInSimpleTree() const override; - QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const override; + bool supportsAction(ProjectExplorer::ProjectAction action, Node *node) const final; bool addFiles(const QStringList &filePaths, QStringList *notAdded = 0) override; bool removeFiles(const QStringList &filePaths, QStringList *notRemoved = 0) override; bool renameFile(const QString &filePath, const QString &newFilePath) override; diff --git a/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp b/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp index 1f2d1dc445..db80627f68 100644 --- a/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp +++ b/src/plugins/qbsprojectmanager/qbsnodetreebuilder.cpp @@ -133,6 +133,10 @@ buildProductNodeTree(const qbs::Project &project, const qbs::ProductData &prd) void setupProjectNode(QbsProjectManager::Internal::QbsProjectNode *node, const qbs::ProjectData &prjData, const qbs::Project &qbsProject) { + using namespace QbsProjectManager::Internal; + node->addNode(new QbsFileNode(Utils::FileName::fromString(prjData.location().filePath()), + ProjectExplorer::FileType::Project, false, + prjData.location().line())); foreach (const qbs::ProjectData &subData, prjData.subProjects()) { auto subProject = new QbsProjectManager::Internal::QbsProjectNode( @@ -169,9 +173,16 @@ QSet<QString> referencedBuildSystemFiles(const qbs::ProjectData &data) QStringList unreferencedBuildSystemFiles(const qbs::Project &p) { - return p.isValid() - ? p.buildSystemFiles().subtract(referencedBuildSystemFiles(p.projectData())).toList() - : QStringList(); + QStringList result; + if (!p.isValid()) + return result; + + const std::set<QString> &available = p.buildSystemFiles(); + QList<QString> referenced = referencedBuildSystemFiles(p.projectData()).toList(); + Utils::sort(referenced); + std::set_difference(available.begin(), available.end(), referenced.begin(), referenced.end(), + std::back_inserter(result)); + return result; } } // namespace @@ -182,9 +193,7 @@ namespace Internal { QbsRootProjectNode *QbsNodeTreeBuilder::buildTree(QbsProject *project) { auto root = new QbsRootProjectNode(project); - root->addNode(new ProjectExplorer::FileNode(project->projectFilePath(), - ProjectExplorer::FileType::Project, false)); - + setupProjectNode(root, project->qbsProjectData(), project->qbsProject()); auto buildSystemFiles = new ProjectExplorer::FolderNode(project->projectDirectory(), ProjectExplorer::NodeType::Folder, @@ -194,12 +203,11 @@ QbsRootProjectNode *QbsNodeTreeBuilder::buildTree(QbsProject *project) for (const QString &f : unreferencedBuildSystemFiles(project->qbsProject())) { const Utils::FileName filePath = Utils::FileName::fromString(f); if (filePath.isChildOf(base)) - root->addNestedNode(new ProjectExplorer::FileNode(filePath, ProjectExplorer::FileType::Project, false)); + buildSystemFiles->addNestedNode(new ProjectExplorer::FileNode(filePath, ProjectExplorer::FileType::Project, false)); } buildSystemFiles->compress(); root->addNode(buildSystemFiles); - setupProjectNode(root, project->qbsProjectData(), project->qbsProject()); return root; } diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp index 9bdd11a611..33c5117765 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.cpp +++ b/src/plugins/qbsprojectmanager/qbsproject.cpp @@ -133,6 +133,8 @@ QbsProject::QbsProject(const FileName &fileName) : setProjectContext(Context(Constants::PROJECT_ID)); setProjectLanguages(Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); + setDisplayName(fileName.toFileInfo().completeBaseName()); + connect(this, &Project::activeTargetChanged, this, &QbsProject::changeActiveTarget); connect(this, &Project::addedTarget, this, &QbsProject::targetWasAdded); connect(this, &Project::removedTarget, this, &QbsProject::targetWasRemoved); @@ -159,11 +161,6 @@ QbsProject::~QbsProject() qDeleteAll(m_extraCompilers); } -QString QbsProject::displayName() const -{ - return projectFilePath().toFileInfo().completeBaseName(); -} - QbsRootProjectNode *QbsProject::rootProjectNode() const { return static_cast<QbsRootProjectNode *>(Project::rootProjectNode()); @@ -434,12 +431,20 @@ bool QbsProject::checkCancelStatus() return true; } +static QSet<QString> toQStringSet(const std::set<QString> &src) +{ + QSet<QString> result; + result.reserve(int(src.size())); + std::copy(src.begin(), src.end(), Utils::inserter(result)); + return result; +} + void QbsProject::updateAfterParse() { qCDebug(qbsPmLog) << "Updating data after parse"; OpTimer opTimer("updateAfterParse"); updateProjectNodes(); - updateDocuments(m_qbsProject.buildSystemFiles()); + updateDocuments(toQStringSet(m_qbsProject.buildSystemFiles())); updateBuildTargetData(); updateCppCodeModel(); updateQmlJsCodeModel(); diff --git a/src/plugins/qbsprojectmanager/qbsproject.h b/src/plugins/qbsprojectmanager/qbsproject.h index 6c787ef449..ea987fd36a 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.h +++ b/src/plugins/qbsprojectmanager/qbsproject.h @@ -61,7 +61,6 @@ public: explicit QbsProject(const Utils::FileName &filename); ~QbsProject() override; - QString displayName() const override; QbsRootProjectNode *rootProjectNode() const override; QStringList filesGeneratedFrom(const QString &sourceFile) const override; diff --git a/src/plugins/qmakeandroidsupport/createandroidmanifestwizard.cpp b/src/plugins/qmakeandroidsupport/createandroidmanifestwizard.cpp index f1cf7cbdae..4140f038bc 100644 --- a/src/plugins/qmakeandroidsupport/createandroidmanifestwizard.cpp +++ b/src/plugins/qmakeandroidsupport/createandroidmanifestwizard.cpp @@ -328,10 +328,13 @@ void CreateAndroidManifestWizard::createAndroidTemplateFiles() FileName::fromString(m_directory), 0, [this, &addedFiles](QFileInfo src, QFileInfo dst, QString *){return copy(src, dst, &addedFiles);}); - if (m_copyGradle) - FileUtils::copyRecursively(AndroidConfigurations::currentConfig().sdkLocation().appendPath(QLatin1String("/tools/templates/gradle/wrapper")), - FileName::fromString(m_directory), + if (m_copyGradle) { + FileName gradlePath = FileName::fromString(version->qmakeProperty("QT_INSTALL_PREFIX").append(QLatin1String("/src/3rdparty/gradle"))); + if (!gradlePath.exists()) + gradlePath = AndroidConfigurations::currentConfig().sdkLocation().appendPath(QLatin1String("/tools/templates/gradle/wrapper")); + FileUtils::copyRecursively(gradlePath, FileName::fromString(m_directory), 0, [this, &addedFiles](QFileInfo src, QFileInfo dst, QString *){return copy(src, dst, &addedFiles);}); + } AndroidManager::updateGradleProperties(m_target); } diff --git a/src/plugins/qmakeandroidsupport/qmakeandroidbuildapkstep.cpp b/src/plugins/qmakeandroidsupport/qmakeandroidbuildapkstep.cpp index c336329c9c..ce087ff9df 100644 --- a/src/plugins/qmakeandroidsupport/qmakeandroidbuildapkstep.cpp +++ b/src/plugins/qmakeandroidsupport/qmakeandroidbuildapkstep.cpp @@ -182,13 +182,6 @@ bool QmakeAndroidBuildApkStep::init(QList<const BuildStep *> &earlierSteps) QStringList argumentsPasswordConcealed = arguments; - if (version->qtVersion() >= QtSupport::QtVersionNumber(5, 6, 0)) { - if (bc->buildType() == ProjectExplorer::BuildConfiguration::Debug) - arguments << QLatin1String("--gdbserver"); - else - arguments << QLatin1String("--no-gdbserver"); - } - if (m_signPackage) { arguments << QLatin1String("--sign") << m_keystorePath.toString() @@ -206,6 +199,15 @@ bool QmakeAndroidBuildApkStep::init(QList<const BuildStep *> &earlierSteps) } + // Must be the last option, otherwise androiddeployqt might use the other + // params (e.g. --sign) to choose not to add gdbserver + if (version->qtVersion() >= QtSupport::QtVersionNumber(5, 6, 0)) { + if (m_addDebugger || bc->buildType() == ProjectExplorer::BuildConfiguration::Debug) + arguments << QLatin1String("--gdbserver"); + else + arguments << QLatin1String("--no-gdbserver"); + } + ProjectExplorer::ProcessParameters *pp = processParameters(); setupProcessParameters(pp, bc, arguments, command); diff --git a/src/plugins/qmakeprojectmanager/desktopqmakerunconfiguration.cpp b/src/plugins/qmakeprojectmanager/desktopqmakerunconfiguration.cpp index 88c6b3f78a..2c9ea0dd02 100644 --- a/src/plugins/qmakeprojectmanager/desktopqmakerunconfiguration.cpp +++ b/src/plugins/qmakeprojectmanager/desktopqmakerunconfiguration.cpp @@ -344,8 +344,8 @@ bool DesktopQmakeRunConfiguration::fromMap(const QVariantMap &map) QString DesktopQmakeRunConfiguration::executable() const { - if (QmakeProFileNode *node = projectNode()) - return extractWorkingDirAndExecutable(node).second; + if (QmakeProFile *pro = proFile()) + return extractWorkingDirAndExecutable(pro).second; return QString(); } @@ -377,18 +377,18 @@ void DesktopQmakeRunConfiguration::setUsingLibrarySearchPath(bool state) QString DesktopQmakeRunConfiguration::baseWorkingDirectory() const { - if (QmakeProFileNode *node = projectNode()) - return extractWorkingDirAndExecutable(node).first; + if (QmakeProFile *pro = proFile()) + return extractWorkingDirAndExecutable(pro).first; return QString(); } bool DesktopQmakeRunConfiguration::isConsoleApplication() const { - if (QmakeProFileNode *node = projectNode()) { - const QStringList config = node->variableValue(Variable::Config); + if (QmakeProFile *pro = proFile()) { + const QStringList config = pro->variableValue(Variable::Config); if (!config.contains("console") || config.contains("testcase")) return false; - const QStringList qt = node->variableValue(Variable::Qt); + const QStringList qt = pro->variableValue(Variable::Qt); return !qt.contains("testlib") && !qt.contains("qmltest"); } return false; @@ -402,11 +402,11 @@ void DesktopQmakeRunConfiguration::addToBaseEnvironment(Environment &env) const // The user could be linking to a library found via a -L/some/dir switch // to find those libraries while actually running we explicitly prepend those // dirs to the library search path - const QmakeProFileNode *node = projectNode(); - if (m_isUsingLibrarySearchPath && node) { - const QStringList libDirectories = node->variableValue(Variable::LibDirectories); + const QmakeProFile *pro = proFile(); + if (m_isUsingLibrarySearchPath && pro) { + const QStringList libDirectories = pro->variableValue(Variable::LibDirectories); if (!libDirectories.isEmpty()) { - const QString proDirectory = node->buildDir(); + const QString proDirectory = pro->buildDir().toString(); foreach (QString dir, libDirectories) { // Fix up relative entries like "LIBS+=-L.." const QFileInfo fi(dir); @@ -415,7 +415,7 @@ void DesktopQmakeRunConfiguration::addToBaseEnvironment(Environment &env) const env.prependOrSetLibrarySearchPath(dir); } // foreach } // libDirectories - } // node + } // pro QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(target()->kit()); if (qtVersion && m_isUsingLibrarySearchPath) @@ -437,20 +437,18 @@ QmakeProject *DesktopQmakeRunConfiguration::qmakeProject() const return static_cast<QmakeProject *>(target()->project()); } -QmakeProFileNode *DesktopQmakeRunConfiguration::projectNode() const +QmakeProFile *DesktopQmakeRunConfiguration::proFile() const { QmakeProject *project = qmakeProject(); QTC_ASSERT(project, return nullptr); - QmakeProFileNode *rootNode = project->rootProjectNode(); - if (!rootNode) - return nullptr; - return rootNode->findProFileFor(m_proFilePath); + QmakeProFile *rootProFile = project->rootProFile(); + return rootProFile ? rootProFile->findProFile(m_proFilePath) : nullptr; } QString DesktopQmakeRunConfiguration::defaultDisplayName() { - if (QmakeProFileNode *node = projectNode()) - return node->displayName(); + if (QmakeProFile *pro = proFile()) + return pro->displayName(); QString defaultName; if (!m_proFilePath.isEmpty()) @@ -465,19 +463,16 @@ OutputFormatter *DesktopQmakeRunConfiguration::createOutputFormatter() const return new QtSupport::QtOutputFormatter(target()->project()); } -QPair<QString, QString> DesktopQmakeRunConfiguration::extractWorkingDirAndExecutable(const QmakeProFileNode *node) const +QPair<QString, QString> DesktopQmakeRunConfiguration::extractWorkingDirAndExecutable(const QmakeProFile *proFile) const { - if (!node) - return { }; + if (!proFile) + return {}; - QmakeProFile *pro = node->proFile(); - QTC_ASSERT(pro, return { }); - - TargetInformation ti = pro->targetInformation(); + TargetInformation ti = proFile->targetInformation(); if (!ti.valid) return qMakePair(QString(), QString()); - const QStringList &config = pro->variableValue(Variable::Config); + const QStringList &config = proFile->variableValue(Variable::Config); QString destDir = ti.destDir.toString(); QString workingDir; diff --git a/src/plugins/qmakeprojectmanager/desktopqmakerunconfiguration.h b/src/plugins/qmakeprojectmanager/desktopqmakerunconfiguration.h index d32cd008a5..18ece5ecd4 100644 --- a/src/plugins/qmakeprojectmanager/desktopqmakerunconfiguration.h +++ b/src/plugins/qmakeprojectmanager/desktopqmakerunconfiguration.h @@ -98,12 +98,12 @@ protected: bool fromMap(const QVariantMap &map) override; private: - QPair<QString, QString> extractWorkingDirAndExecutable(const QmakeProFileNode *node) const; + QPair<QString, QString> extractWorkingDirAndExecutable(const QmakeProFile *proFile) const; QString baseWorkingDirectory() const; QString defaultDisplayName(); bool isConsoleApplication() const; QmakeProject *qmakeProject() const; - QmakeProFileNode *projectNode() const; + QmakeProFile *proFile() const; void ctor(); diff --git a/src/plugins/qmakeprojectmanager/qmakenodes.cpp b/src/plugins/qmakeprojectmanager/qmakenodes.cpp index b2ea316330..98c0cae52e 100644 --- a/src/plugins/qmakeprojectmanager/qmakenodes.cpp +++ b/src/plugins/qmakeprojectmanager/qmakenodes.cpp @@ -74,9 +74,13 @@ QmakeProFileNode *QmakePriFileNode::proFileNode() const return m_qmakeProFileNode; } -QList<ProjectAction> QmakePriFileNode::supportedActions(Node *node) const +bool QmakePriFileNode::supportsAction(ProjectAction action, Node *node) const { - QList<ProjectAction> actions; + if (action == Rename || action == DuplicateFile) { + FileNode *fileNode = node->asFileNode(); + return (fileNode && fileNode->fileType() != FileType::Project) + || dynamic_cast<ResourceEditor::ResourceTopLevelNode *>(node); + } const FolderNode *folderNode = this; const QmakeProFileNode *proFileNode; @@ -93,12 +97,12 @@ QList<ProjectAction> QmakePriFileNode::supportedActions(Node *node) const // TODO: Some of the file types don't make much sense for aux // projects (e.g. cpp). It'd be nice if the "add" action could // work on a subset of the file types according to project type. - - actions << AddNewFile; - if (pro && pro->knowsFile(node->filePath())) - actions << EraseFile; - else - actions << RemoveFile; + if (action == AddNewFile) + return true; + if (action == EraseFile) + return pro && pro->knowsFile(node->filePath()); + if (action == RemoveFile) + return !(pro && pro->knowsFile(node->filePath())); bool addExistingFiles = true; if (node->nodeType() == NodeType::VirtualFolder) { @@ -115,31 +119,26 @@ QList<ProjectAction> QmakePriFileNode::supportedActions(Node *node) const addExistingFiles = addExistingFiles && !deploysFolder(node->filePath().toString()); - if (addExistingFiles) - actions << AddExistingFile << AddExistingDirectory; + if (action == AddExistingFile || action == AddExistingDirectory) + return addExistingFiles; break; } case ProjectType::SubDirsTemplate: - actions << AddSubProject << RemoveSubProject; + if (action == AddSubProject || action == RemoveSubProject) + return true; break; default: break; } - FileNode *fileNode = node->asFileNode(); - if ((fileNode && fileNode->fileType() != FileType::Project) - || dynamic_cast<ResourceEditor::ResourceTopLevelNode *>(node)) { - actions << Rename; - actions << DuplicateFile; + if (action == HasSubProjectRunConfigurations) { + Target *target = m_project->activeTarget(); + QmakeRunConfigurationFactory *factory = QmakeRunConfigurationFactory::find(target); + return factory && !factory->runConfigurationsForNode(target, node).isEmpty(); } - Target *target = m_project->activeTarget(); - QmakeRunConfigurationFactory *factory = QmakeRunConfigurationFactory::find(target); - if (factory && !factory->runConfigurationsForNode(target, node).isEmpty()) - actions << HasSubProjectRunConfigurations; - - return actions; + return false; } bool QmakePriFileNode::canAddSubProject(const QString &proFilePath) const diff --git a/src/plugins/qmakeprojectmanager/qmakenodes.h b/src/plugins/qmakeprojectmanager/qmakenodes.h index b04751809a..719e42a05a 100644 --- a/src/plugins/qmakeprojectmanager/qmakenodes.h +++ b/src/plugins/qmakeprojectmanager/qmakenodes.h @@ -47,7 +47,7 @@ public: QmakePriFile *priFile() const; // ProjectNode interface - QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const override; + bool supportsAction(ProjectExplorer::ProjectAction action, Node *node) const override; bool showInSimpleTree() const override { return false; } diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index d2ce349211..b335fc2484 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -74,6 +74,8 @@ using namespace Utils; namespace QmakeProjectManager { namespace Internal { +const int UPDATE_INTERVAL = 3000; + /// Watches folders for QmakePriFile nodes /// use one file system watcher to watch all folders /// such minimizing system ressouce usage @@ -167,12 +169,13 @@ QmakeProject::QmakeProject(const FileName &fileName) : setProjectContext(Core::Context(QmakeProjectManager::Constants::PROJECT_ID)); setProjectLanguages(Core::Context(ProjectExplorer::Constants::CXX_LANGUAGE_ID)); setRequiredKitPredicate(QtSupport::QtKitInformation::qtVersionPredicate()); + setDisplayName(fileName.toFileInfo().completeBaseName()); const QTextCodec *codec = Core::EditorManager::defaultTextCodec(); m_qmakeVfs->setTextCodec(codec); m_asyncUpdateTimer.setSingleShot(true); - m_asyncUpdateTimer.setInterval(3000); + m_asyncUpdateTimer.setInterval(UPDATE_INTERVAL); connect(&m_asyncUpdateTimer, &QTimer::timeout, this, &QmakeProject::asyncUpdate); m_rootProFile = std::make_unique<QmakeProFile>(this, projectFilePath()); @@ -480,7 +483,8 @@ void QmakeProject::scheduleAsyncUpdate(QmakeProFile::AsyncUpdateDelay delay) void QmakeProject::startAsyncTimer(QmakeProFile::AsyncUpdateDelay delay) { m_asyncUpdateTimer.stop(); - m_asyncUpdateTimer.setInterval(qMin(m_asyncUpdateTimer.interval(), delay == QmakeProFile::ParseLater ? 3000 : 0)); + m_asyncUpdateTimer.setInterval(qMin(m_asyncUpdateTimer.interval(), + delay == QmakeProFile::ParseLater ? UPDATE_INTERVAL : 0)); m_asyncUpdateTimer.start(); } @@ -533,7 +537,7 @@ bool QmakeProject::wasEvaluateCanceled() void QmakeProject::asyncUpdate() { - m_asyncUpdateTimer.setInterval(3000); + m_asyncUpdateTimer.setInterval(UPDATE_INTERVAL); m_qmakeVfs->invalidateCache(); @@ -572,11 +576,6 @@ bool QmakeProject::supportsKit(Kit *k, QString *errorMessage) const return version; } -QString QmakeProject::displayName() const -{ - return projectFilePath().toFileInfo().completeBaseName(); -} - // Find the folder that contains a file with a certain name (recurse down) static FolderNode *folderOf(FolderNode *in, const FileName &fileName) { @@ -1215,14 +1214,15 @@ void QmakeProject::collectLibraryData(const QmakeProFile *file, DeploymentData & QString version = file->singleVariableValue(Variable::Version); if (version.isEmpty()) version = QLatin1String("1.0.0"); + QStringList versionComponents = version.split('.'); + while (versionComponents.size() < 3) + versionComponents << QLatin1String("0"); targetFileName += QLatin1Char('.'); - while (true) { + while (!versionComponents.isEmpty()) { + const QString versionString = versionComponents.join(QLatin1Char('.')); deploymentData.addFile(destDirFor(ti).toString() + '/' - + targetFileName + version, targetPath); - const QString tmpVersion = version.left(version.lastIndexOf(QLatin1Char('.'))); - if (tmpVersion == version) - break; - version = tmpVersion; + + targetFileName + versionString, targetPath); + versionComponents.removeLast(); } } } diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.h b/src/plugins/qmakeprojectmanager/qmakeproject.h index 2f4fb8a427..590fe4f46e 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.h +++ b/src/plugins/qmakeprojectmanager/qmakeproject.h @@ -65,8 +65,6 @@ public: QmakeProFile *rootProFile() const; - QString displayName() const final; - bool supportsKit(ProjectExplorer::Kit *k, QString *errorMesage) const final; QmakeProFileNode *rootProjectNode() const final; diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 61af942312..6b0015f082 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -70,7 +70,7 @@ const char moveToComponentCommandId[] = "MoveToComponent"; const char addItemToStackedContainerCommandId[] = "AddItemToStackedContainer"; const char addTabBarToStackedContainerCommandId[] = "AddTabBarToStackedContainer"; const char increaseIndexOfStackedContainerCommandId[] = "IncreaseIndexOfStackedContainer"; -const char decreaseIndexOfStackedContainerCommandId[] = "decreaseIndexOfStackedContainer"; +const char decreaseIndexOfStackedContainerCommandId[] = "DecreaseIndexOfStackedContainer"; const char selectionCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Selection"); const char stackCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Stack (z)"); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 9375f40503..da2aeb6641 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -898,7 +898,7 @@ void static setIndexProperty(const AbstractProperty &property, const QVariant &v const QString propertyName = QString::fromUtf8(property.name()); - QString title = QCoreApplication::translate("ModelNodeOperations", "Cannot set property %1.").arg(propertyName); + QString title = QCoreApplication::translate("ModelNodeOperations", "Cannot Set Property %1").arg(propertyName); QString description = QCoreApplication::translate("ModelNodeOperations", "The property %1 is bound to an expression.").arg(propertyName); Core::AsynchronousMessageBox::warning(title, description); } diff --git a/src/plugins/qmldesigner/components/importmanager/importmanagercombobox.cpp b/src/plugins/qmldesigner/components/importmanager/importmanagercombobox.cpp index e2204cae32..b703f780c8 100644 --- a/src/plugins/qmldesigner/components/importmanager/importmanagercombobox.cpp +++ b/src/plugins/qmldesigner/components/importmanager/importmanagercombobox.cpp @@ -27,11 +27,15 @@ #include <utils/fileutils.h> +#include <QStyle> +#include <QStyleFactory> #include <QStylePainter> ImportManagerComboBox::ImportManagerComboBox(QWidget *parent) : QComboBox(parent) { + QStyle *style = QStyleFactory::create("fusion"); + setStyle(style); setStyleSheet(QString::fromUtf8(Utils::FileReader::fetchQrc(QLatin1String(":/importmanager/importmanager.css")))); setToolTip(tr("Add new import")); } diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 6f8968cda2..3a0a602b5b 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -380,6 +380,7 @@ void NavigatorTreeModel::updateItemRow(const ModelNode &modelNode, ItemRow items items.idItem->setToolTip(QString::fromUtf8(modelNode.type())); else items.idItem->setToolTip(msgUnknownItem(QString::fromUtf8(modelNode.type()))); + items.idItem->setIcon(getTypeIcon(modelNode)); } blockItemChangedSignal(blockSignal); diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp index b7f20e77a2..9ac4a66aaf 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp @@ -40,6 +40,7 @@ #include <coreplugin/editormanager/editormanager.h> #include <QEvent> +#include <QScrollBar> #include <QVBoxLayout> #include <vector> @@ -61,7 +62,6 @@ TextEditorWidget::TextEditorWidget(TextEditorView *textEditorView) m_updateSelectionTimer.setInterval(200); connect(&m_updateSelectionTimer, &QTimer::timeout, this, &TextEditorWidget::updateSelectionByCursorPosition); - setStyleSheet(Theme::replaceCssColors(QString::fromUtf8(Utils::FileReader::fetchQrc(QLatin1String(":/qmldesigner/scrollbar.css"))))); } void TextEditorWidget::setTextEditor(TextEditor::BaseTextEditor *textEditor) @@ -85,6 +85,9 @@ void TextEditorWidget::setTextEditor(TextEditor::BaseTextEditor *textEditor) }); textEditor->editorWidget()->installEventFilter(this); + static QString styleSheet = Theme::replaceCssColors(QString::fromUtf8(Utils::FileReader::fetchQrc(QLatin1String(":/qmldesigner/scrollbar.css")))); + textEditor->editorWidget()->verticalScrollBar()->setStyleSheet(styleSheet); + textEditor->editorWidget()->horizontalScrollBar()->setStyleSheet(styleSheet); } if (oldEditor) diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h index 53f4045264..b99a9815bd 100644 --- a/src/plugins/qmldesigner/designercore/include/rewriterview.h +++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h @@ -116,6 +116,8 @@ public: void clearErrorAndWarnings(); void setErrors(const QList<DocumentMessage> &errors); void setWarnings(const QList<DocumentMessage> &warnings); + void setIncompleteTypeInformation(bool b); + bool hasIncompleteTypeInformation() const; void addError(const DocumentMessage &error); void enterErrorState(const QString &errorMessage); @@ -195,6 +197,7 @@ private: //variables QTimer m_amendTimer; bool m_instantQmlTextUpdate = false; std::function<void(bool)> m_setWidgetStatusCallback; + bool m_hasIncompleteTypeInformation = false; }; } //QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index dd9b66d57b..a88184bbbd 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -199,6 +199,10 @@ void NodeInstanceView::handleCrash() void NodeInstanceView::restartProcess() { + if (rootNodeInstance().isValid()) + rootNodeInstance().setError({}); + emitInstanceErrorChange({}); + if (m_restartProcessTimerId) killTimer(m_restartProcessTimerId); diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index f1665484b3..a5ff121584 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -371,6 +371,9 @@ bool itemIsMovable(const ModelNode &modelNode) if (modelNode.metaInfo().isSubclassOf("QtQuick.Controls.Tab")) return false; + if (!modelNode.hasParentProperty()) + return false; + if (!modelNode.parentProperty().isNodeListProperty()) return false; diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 6996a3f00b..d550e82fbd 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -86,6 +86,11 @@ void RewriterView::modelAttached(Model *model) if (!(m_errors.isEmpty() && m_warnings.isEmpty())) notifyErrorsAndWarnings(m_errors); + + if (hasIncompleteTypeInformation()) + QTimer::singleShot(1000, this, [this, model](){ + modelAttached(model); + }); } void RewriterView::modelAboutToBeDetached(Model * /*model*/) @@ -465,6 +470,16 @@ void RewriterView::setWarnings(const QList<DocumentMessage> &warnings) notifyErrorsAndWarnings(m_errors); } +void RewriterView::setIncompleteTypeInformation(bool b) +{ + m_hasIncompleteTypeInformation = b; +} + +bool RewriterView::hasIncompleteTypeInformation() const +{ + return m_hasIncompleteTypeInformation; +} + void RewriterView::setErrors(const QList<DocumentMessage> &errors) { m_errors = errors; diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index c66b4ef6a8..af2d24ce95 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -928,6 +928,7 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH const QUrl url = m_rewriterView->model()->fileUrl(); setActive(true); + m_rewriterView->setIncompleteTypeInformation(false); try { Snapshot snapshot = m_rewriterView->textModifier()->qmljsSnapshot(); @@ -1953,6 +1954,9 @@ void TextToModelMerger::setupComponent(const ModelNode &node) void TextToModelMerger::collectLinkErrors(QList<DocumentMessage> *errors, const ReadingContext &ctxt) { foreach (const QmlJS::DiagnosticMessage &diagnosticMessage, ctxt.diagnosticLinkMessages()) { + if (diagnosticMessage.kind == QmlJS::Severity::ReadingTypeInfoWarning) + m_rewriterView->setIncompleteTypeInformation(true); + errors->append(DocumentMessage(diagnosticMessage, QUrl::fromLocalFile(m_document->fileName()))); } } diff --git a/src/plugins/qmldesigner/designmodewidget.cpp b/src/plugins/qmldesigner/designmodewidget.cpp index 33056bf9be..e1bff9bfb6 100644 --- a/src/plugins/qmldesigner/designmodewidget.cpp +++ b/src/plugins/qmldesigner/designmodewidget.cpp @@ -454,10 +454,6 @@ static Core::MiniSplitter *createCentralSplitter(const QList<WidgetInfo> &widget SwitchSplitTabWidget *switchSplitTabWidget = new SwitchSplitTabWidget(); - QString sheet = QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/centerwidget.css")); - switchSplitTabWidget->setStyleSheet(Theme::replaceCssColors(sheet)); - - foreach (const WidgetInfo &widgetInfo, widgetInfos) { if (widgetInfo.placementHint == widgetInfo.CentralPane) switchSplitTabWidget->addTab(widgetInfo.widget, widgetInfo.tabName); diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp index 357420c1d8..abf45bfbb1 100644 --- a/src/plugins/qmldesigner/documentmanager.cpp +++ b/src/plugins/qmldesigner/documentmanager.cpp @@ -438,7 +438,7 @@ bool DocumentManager::isoProFileSupportsAddingExistingFiles(const QString &resou ProjectExplorer::ProjectNode *projectNode = node->parentFolderNode()->asProjectNode(); if (!projectNode) return false; - if (!projectNode->supportedActions(projectNode).contains(ProjectExplorer::AddExistingFile)) { + if (!projectNode->supportsAction(ProjectExplorer::AddExistingFile, projectNode)) { qCWarning(documentManagerLog) << "Project" << projectNode->displayName() << "does not support adding existing files"; return false; } diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index b7eb2ce63e..37bdd3402d 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -415,6 +415,7 @@ void QmlDesignerPlugin::deactivateAutoSynchronization() viewManager().detachViewsExceptRewriterAndComponetView(); viewManager().detachComponentView(); viewManager().detachRewriterView(); + emitCurrentTextEditorChanged(documentManager().currentDesignDocument()->textEditor()); documentManager().currentDesignDocument()->resetToDocumentModel(); } diff --git a/src/plugins/qmldesigner/switchsplittabwidget.cpp b/src/plugins/qmldesigner/switchsplittabwidget.cpp index 08ad71ef12..6fcc98beb5 100644 --- a/src/plugins/qmldesigner/switchsplittabwidget.cpp +++ b/src/plugins/qmldesigner/switchsplittabwidget.cpp @@ -24,8 +24,10 @@ ****************************************************************************/ #include "switchsplittabwidget.h" +#include <theme.h> #include <utils/utilsicons.h> +#include <utils/fileutils.h> #include <QVector> #include <QBoxLayout> @@ -48,6 +50,9 @@ SwitchSplitTabWidget::SwitchSplitTabWidget(QWidget *parent) m_splitter->setObjectName("centralTabWidget"); m_splitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + QString sheet = QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/centerwidget.css")); + m_tabBarBackground->setStyleSheet(Theme::replaceCssColors(sheet)); + m_tabBar->setObjectName("centralTabBar"); m_tabBar->setShape(QTabBar::RoundedEast); m_tabBar->setDocumentMode(false); diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index f68a9d0e4a..cf84adfae4 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -105,10 +105,7 @@ namespace Internal { QmlJSEditorWidget::QmlJSEditorWidget() { - m_outlineCombo = 0; - m_contextPane = 0; m_findReferences = new FindReferences(this); - setLanguageSettingsId(QmlJSTools::Constants::QML_JS_SETTINGS_ID); } @@ -142,7 +139,6 @@ void QmlJSEditorWidget::finalizeInitialization() &m_contextPaneTimer, static_cast<void (QTimer::*)()>(&QTimer::start)); connect(m_contextPane, &IContextPane::closed, this, &QmlJSEditorWidget::showTextMarker); } - m_oldCursorPosition = -1; connect(this->document(), &QTextDocument::modificationChanged, this, &QmlJSEditorWidget::modificationChanged); @@ -161,7 +157,6 @@ QModelIndex QmlJSEditorWidget::outlineModelIndex() { if (!m_outlineModelIndex.isValid()) { m_outlineModelIndex = indexForPosition(position()); - emit outlineModelIndexChanged(m_outlineModelIndex); } return m_outlineModelIndex; } @@ -223,6 +218,11 @@ void QmlJSEditorWidget::modificationChanged(bool changed) m_modelManager->fileChangedOnDisk(textDocument()->filePath().toString()); } +bool QmlJSEditorWidget::isOutlineCursorChangesBlocked() +{ + return hasFocus() || m_blockOutLineCursorChanges; +} + void QmlJSEditorWidget::jumpToOutlineElement(int /*index*/) { QModelIndex index = m_outlineCombo->view()->currentIndex(); @@ -253,6 +253,7 @@ void QmlJSEditorWidget::updateOutlineIndexNow() m_outlineModelIndex = QModelIndex(); // invalidate QModelIndex comboIndex = outlineModelIndex(); + emit outlineModelIndexChanged(m_outlineModelIndex); if (comboIndex.isValid()) { bool blocked = m_outlineCombo->blockSignals(true); @@ -504,18 +505,6 @@ QString QmlJSEditorWidget::wordUnderCursor() const return word; } -bool QmlJSEditorWidget::isClosingBrace(const QList<Token> &tokens) const -{ - - if (tokens.size() == 1) { - const Token firstToken = tokens.first(); - - return firstToken.is(Token::RightBrace) || firstToken.is(Token::RightBracket); - } - - return false; -} - void QmlJSEditorWidget::createToolBar() { m_outlineCombo = new QComboBox; @@ -546,8 +535,6 @@ void QmlJSEditorWidget::createToolBar() this, &QmlJSEditorWidget::jumpToOutlineElement); connect(m_qmlJsEditorDocument->outlineModel(), &QmlOutlineModel::updated, static_cast<QTreeView *>(m_outlineCombo->view()), &QTreeView::expandAll); - connect(m_qmlJsEditorDocument->outlineModel(), &QmlOutlineModel::updated, - this, &QmlJSEditorWidget::updateOutlineIndexNow); connect(this, &QmlJSEditorWidget::cursorPositionChanged, &m_updateOutlineIndexTimer, static_cast<void (QTimer::*)()>(&QTimer::start)); @@ -826,6 +813,7 @@ void QmlJSEditorWidget::showContextPane() void QmlJSEditorWidget::contextMenuEvent(QContextMenuEvent *e) { + m_blockOutLineCursorChanges = true; QPointer<QMenu> menu(new QMenu(this)); QMenu *refactoringMenu = new QMenu(tr("Refactoring"), menu); @@ -870,6 +858,7 @@ void QmlJSEditorWidget::contextMenuEvent(QContextMenuEvent *e) menu->exec(e->globalPos()); delete menu; + m_blockOutLineCursorChanges = false; } bool QmlJSEditorWidget::event(QEvent *e) diff --git a/src/plugins/qmljseditor/qmljseditor.h b/src/plugins/qmljseditor/qmljseditor.h index 9a086e65c0..2710e29fa0 100644 --- a/src/plugins/qmljseditor/qmljseditor.h +++ b/src/plugins/qmljseditor/qmljseditor.h @@ -66,6 +66,8 @@ public: QmlJSEditorDocument *qmlJsEditorDocument() const; QModelIndex outlineModelIndex(); + void updateOutlineIndexNow(); + bool isOutlineCursorChangesBlocked(); TextEditor::AssistInterface *createAssistInterface(TextEditor::AssistKind assistKind, TextEditor::AssistReason reason) const override; @@ -84,7 +86,6 @@ private: void modificationChanged(bool); void jumpToOutlineElement(int index); - void updateOutlineIndexNow(); void updateContextPane(); void showTextMarker(); @@ -109,24 +110,23 @@ protected: void onRefactorMarkerClicked(const TextEditor::RefactorMarker &marker) override; private: - bool isClosingBrace(const QList<QmlJS::Token> &tokens) const; - void setSelectedElements(); QString wordUnderCursor() const; QModelIndex indexForPosition(unsigned cursorPosition, const QModelIndex &rootIndex = QModelIndex()) const; bool hideContextPane(); - QmlJSEditorDocument *m_qmlJsEditorDocument; + QmlJSEditorDocument *m_qmlJsEditorDocument = nullptr; QTimer m_updateUsesTimer; // to wait for multiple text cursor position changes QTimer m_updateOutlineIndexTimer; QTimer m_contextPaneTimer; QComboBox *m_outlineCombo; QModelIndex m_outlineModelIndex; - QmlJS::ModelManagerInterface *m_modelManager; + bool m_blockOutLineCursorChanges = false; + QmlJS::ModelManagerInterface *m_modelManager = nullptr; - QmlJS::IContextPane *m_contextPane; - int m_oldCursorPosition; + QmlJS::IContextPane *m_contextPane = nullptr; + int m_oldCursorPosition = -1; FindReferences *m_findReferences; }; diff --git a/src/plugins/qmljseditor/qmljseditordocument.cpp b/src/plugins/qmljseditor/qmljseditordocument.cpp index 6ef9f3bdee..732deddb4b 100644 --- a/src/plugins/qmljseditor/qmljseditordocument.cpp +++ b/src/plugins/qmljseditor/qmljseditordocument.cpp @@ -455,12 +455,9 @@ namespace QmlJSEditor { namespace Internal { QmlJSEditorDocumentPrivate::QmlJSEditorDocumentPrivate(QmlJSEditorDocument *parent) - : q(parent), - m_semanticInfoDocRevision(-1), - m_semanticHighlighter(new SemanticHighlighter(parent)), - m_semanticHighlightingNecessary(false), - m_outlineModelNeedsUpdate(false), - m_outlineModel(new QmlOutlineModel(parent)) + : q(parent) + , m_semanticHighlighter(new SemanticHighlighter(parent)) + , m_outlineModel(new QmlOutlineModel(parent)) { ModelManagerInterface *modelManager = ModelManagerInterface::instance(); diff --git a/src/plugins/qmljseditor/qmljseditordocument_p.h b/src/plugins/qmljseditor/qmljseditordocument_p.h index f2cee06d76..854b81519f 100644 --- a/src/plugins/qmljseditor/qmljseditordocument_p.h +++ b/src/plugins/qmljseditor/qmljseditordocument_p.h @@ -58,19 +58,19 @@ public: void updateOutlineModel(); public: - QmlJSEditorDocument *q; + QmlJSEditorDocument *q = nullptr; QTimer m_updateDocumentTimer; // used to compress multiple document changes QTimer m_reupdateSemanticInfoTimer; // used to compress multiple libraryInfo changes - int m_semanticInfoDocRevision; // document revision to which the semantic info is currently updated to + int m_semanticInfoDocRevision = -1; // document revision to which the semantic info is currently updated to SemanticInfoUpdater *m_semanticInfoUpdater; QmlJSTools::SemanticInfo m_semanticInfo; QVector<QTextLayout::FormatRange> m_diagnosticRanges; - Internal::SemanticHighlighter *m_semanticHighlighter; - bool m_semanticHighlightingNecessary; - bool m_outlineModelNeedsUpdate; + Internal::SemanticHighlighter *m_semanticHighlighter = nullptr; + bool m_semanticHighlightingNecessary = false; + bool m_outlineModelNeedsUpdate = false; bool m_firstSementicInfo = true; QTimer m_updateOutlineModelTimer; - Internal::QmlOutlineModel *m_outlineModel; + Internal::QmlOutlineModel *m_outlineModel = nullptr; }; } // Internal diff --git a/src/plugins/qmljseditor/qmljsoutline.cpp b/src/plugins/qmljseditor/qmljsoutline.cpp index bf37940983..ffe938e543 100644 --- a/src/plugins/qmljseditor/qmljsoutline.cpp +++ b/src/plugins/qmljseditor/qmljsoutline.cpp @@ -94,13 +94,10 @@ void QmlJSOutlineFilterModel::setFilterBindings(bool filterBindings) invalidateFilter(); } -QmlJSOutlineWidget::QmlJSOutlineWidget(QWidget *parent) : - TextEditor::IOutlineWidget(parent), - m_treeView(new QmlJSOutlineTreeView(this)), - m_filterModel(new QmlJSOutlineFilterModel(this)), - m_editor(0), - m_enableCursorSync(true), - m_blockCursorSync(false) +QmlJSOutlineWidget::QmlJSOutlineWidget(QWidget *parent) + : TextEditor::IOutlineWidget(parent) + , m_treeView(new QmlJSOutlineTreeView(this)) + , m_filterModel(new QmlJSOutlineFilterModel(this)) { m_filterModel->setFilterBindings(false); @@ -127,7 +124,7 @@ void QmlJSOutlineWidget::setEditor(QmlJSEditorWidget *editor) m_editor = editor; m_filterModel->setSourceModel(m_editor->qmlJsEditorDocument()->outlineModel()); - modelUpdated(); + m_treeView->expandAll(); connect(m_treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QmlJSOutlineWidget::updateSelectionInText); @@ -137,22 +134,21 @@ void QmlJSOutlineWidget::setEditor(QmlJSEditorWidget *editor) connect(m_editor, &QmlJSEditorWidget::outlineModelIndexChanged, this, &QmlJSOutlineWidget::updateSelectionInTree); - connect(m_editor->qmlJsEditorDocument()->outlineModel(), &QmlOutlineModel::updated, - this, &QmlJSOutlineWidget::modelUpdated); + connect(m_editor->qmlJsEditorDocument()->outlineModel(), &QmlOutlineModel::updated, this, [this] () { + m_treeView->expandAll(); + m_editor->updateOutlineIndexNow(); + }); } QList<QAction*> QmlJSOutlineWidget::filterMenuActions() const { - QList<QAction*> list; - list.append(m_showBindingsAction); - return list; + return {m_showBindingsAction}; } void QmlJSOutlineWidget::setCursorSynchronization(bool syncWithCursor) { m_enableCursorSync = syncWithCursor; - if (m_enableCursorSync) - updateSelectionInTree(m_editor->outlineModelIndex()); + m_editor->updateOutlineIndexNow(); } void QmlJSOutlineWidget::restoreSettings(const QVariantMap &map) @@ -163,14 +159,7 @@ void QmlJSOutlineWidget::restoreSettings(const QVariantMap &map) QVariantMap QmlJSOutlineWidget::settings() const { - QVariantMap map; - map.insert(QLatin1String("QmlJSOutline.ShowBindings"), m_showBindingsAction->isChecked()); - return map; -} - -void QmlJSOutlineWidget::modelUpdated() -{ - m_treeView->expandAll(); + return {{QLatin1String("QmlJSOutline.ShowBindings"), m_showBindingsAction->isChecked()}}; } void QmlJSOutlineWidget::updateSelectionInTree(const QModelIndex &index) @@ -206,27 +195,29 @@ void QmlJSOutlineWidget::updateSelectionInText(const QItemSelection &selection) void QmlJSOutlineWidget::updateTextCursor(const QModelIndex &index) { - QModelIndex sourceIndex = m_filterModel->mapToSource(index); - AST::SourceLocation location - = m_editor->qmlJsEditorDocument()->outlineModel()->sourceLocation(sourceIndex); - - if (!location.isValid()) - return; - - const QTextBlock lastBlock = m_editor->document()->lastBlock(); - const uint textLength = lastBlock.position() + lastBlock.length(); - if (location.offset >= textLength) - return; - - Core::EditorManager::cutForwardNavigationHistory(); - Core::EditorManager::addCurrentPositionToNavigationHistory(); - - QTextCursor textCursor = m_editor->textCursor(); - m_blockCursorSync = true; - textCursor.setPosition(location.offset); - m_editor->setTextCursor(textCursor); - m_editor->centerCursor(); - m_blockCursorSync = false; + if (!m_editor->isOutlineCursorChangesBlocked()) { + QModelIndex sourceIndex = m_filterModel->mapToSource(index); + AST::SourceLocation location + = m_editor->qmlJsEditorDocument()->outlineModel()->sourceLocation(sourceIndex); + + if (!location.isValid()) + return; + + const QTextBlock lastBlock = m_editor->document()->lastBlock(); + const uint textLength = lastBlock.position() + lastBlock.length(); + if (location.offset >= textLength) + return; + + Core::EditorManager::cutForwardNavigationHistory(); + Core::EditorManager::addCurrentPositionToNavigationHistory(); + + QTextCursor textCursor = m_editor->textCursor(); + m_blockCursorSync = true; + textCursor.setPosition(location.offset); + m_editor->setTextCursor(textCursor); + m_editor->centerCursor(); + m_blockCursorSync = false; + } } void QmlJSOutlineWidget::focusEditor() @@ -237,8 +228,8 @@ void QmlJSOutlineWidget::focusEditor() void QmlJSOutlineWidget::setShowBindings(bool showBindings) { m_filterModel->setFilterBindings(!showBindings); - modelUpdated(); - updateSelectionInTree(m_editor->outlineModelIndex()); + m_treeView->expandAll(); + m_editor->updateOutlineIndexNow(); } bool QmlJSOutlineWidget::syncCursor() diff --git a/src/plugins/qmljseditor/qmljsoutline.h b/src/plugins/qmljseditor/qmljsoutline.h index 799aaf812a..a8f15c53e3 100644 --- a/src/plugins/qmljseditor/qmljsoutline.h +++ b/src/plugins/qmljseditor/qmljsoutline.h @@ -31,6 +31,10 @@ #include <QSortFilterProxyModel> +QT_BEGIN_NAMESPACE +class QAction; +QT_END_NAMESPACE + namespace Core { class IEditor; } namespace QmlJS { class Editor; } @@ -66,13 +70,12 @@ public: void setEditor(QmlJSEditorWidget *editor); // IOutlineWidget - virtual QList<QAction*> filterMenuActions() const; - virtual void setCursorSynchronization(bool syncWithCursor); - virtual void restoreSettings(const QVariantMap &map); - virtual QVariantMap settings() const; + virtual QList<QAction*> filterMenuActions() const override; + virtual void setCursorSynchronization(bool syncWithCursor) override; + virtual void restoreSettings(const QVariantMap &map) override; + virtual QVariantMap settings() const override; private: - void modelUpdated(); void updateSelectionInTree(const QModelIndex &index); void updateSelectionInText(const QItemSelection &selection); void updateTextCursor(const QModelIndex &index); @@ -81,14 +84,14 @@ private: bool syncCursor(); private: - QmlJSOutlineTreeView *m_treeView; - QmlJSOutlineFilterModel *m_filterModel; - QmlJSEditorWidget *m_editor; + QmlJSOutlineTreeView *m_treeView = nullptr; + QmlJSOutlineFilterModel *m_filterModel = nullptr; + QmlJSEditorWidget *m_editor = nullptr; - QAction *m_showBindingsAction; + QAction *m_showBindingsAction = nullptr; - bool m_enableCursorSync; - bool m_blockCursorSync; + bool m_enableCursorSync = true; + bool m_blockCursorSync = false; }; class QmlJSOutlineWidgetFactory : public TextEditor::IOutlineWidgetFactory diff --git a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp index d2a855062f..4cd696a046 100644 --- a/src/plugins/qmljseditor/qmljssemantichighlighter.cpp +++ b/src/plugins/qmljseditor/qmljssemantichighlighter.cpp @@ -436,7 +436,9 @@ protected: const TextEditor::FontSettings &fontSettings = TextEditor::TextEditorSettings::instance()->fontSettings(); QTextCharFormat format; - if (d.severity == Severity::Warning || d.severity == Severity::MaybeWarning) { + if (d.severity == Severity::Warning + || d.severity == Severity::MaybeWarning + || d.severity == Severity::ReadingTypeInfoWarning) { format = fontSettings.toTextCharFormat(TextEditor::C_WARNING); } else if (d.severity == Severity::Error || d.severity == Severity::MaybeError) { format = fontSettings.toTextCharFormat(TextEditor::C_ERROR); diff --git a/src/plugins/qmlprofiler/tests/flamegraphview_test.cpp b/src/plugins/qmlprofiler/tests/flamegraphview_test.cpp index 799f3c3ac1..5d94a1a8b1 100644 --- a/src/plugins/qmlprofiler/tests/flamegraphview_test.cpp +++ b/src/plugins/qmlprofiler/tests/flamegraphview_test.cpp @@ -116,38 +116,42 @@ void FlameGraphViewTest::testContextMenu() targetHeight = (testMenu.height() + prevHeight) / 2; } + QTest::mouseMove(&view, QPoint(250, 250)); + QSignalSpy spy(&view, SIGNAL(showFullRange())); + QTimer timer; timer.setInterval(50); - timer.start(); + int menuClicks = 0; connect(&timer, &QTimer::timeout, this, [&]() { auto activePopup = qApp->activePopupWidget(); - if (!activePopup || !activePopup->windowHandle()->isExposed()) + if (!activePopup || !activePopup->windowHandle()->isExposed()) { + QContextMenuEvent *event = new QContextMenuEvent(QContextMenuEvent::Mouse, + QPoint(250, 250)); + qApp->postEvent(&view, event); return; + } + QTest::mouseMove(activePopup, QPoint(targetWidth, targetHeight)); QTest::mouseClick(activePopup, Qt::LeftButton, Qt::NoModifier, QPoint(targetWidth, targetHeight)); + ++menuClicks; if (!manager.isRestrictedToRange()) { // click somewhere else to remove the menu and return to outer function - QTest::mouseClick(qApp->activePopupWidget(), Qt::LeftButton, Qt::NoModifier, - QPoint(500, 500)); + QTest::mouseMove(activePopup, QPoint(-10, -10)); + QTest::mouseClick(activePopup, Qt::LeftButton, Qt::NoModifier, QPoint(-10, -10)); } }); - QTest::mouseMove(&view, QPoint(250, 250)); - QSignalSpy spy(&view, SIGNAL(showFullRange())); - - QContextMenuEvent event(QContextMenuEvent::Mouse, QPoint(250, 250)); - QVERIFY(qApp->notify(&view, &event)); + timer.start(); + QTRY_VERIFY(menuClicks > 0); QCOMPARE(spy.count(), 0); - manager.restrictToRange(1, 10); - - QVERIFY(qApp->notify(&view, &event)); - - if (spy.count() != 1) - QTRY_COMPARE(spy.count(), 1); + QVERIFY(manager.isRestrictedToRange()); + QTRY_COMPARE(spy.count(), 1); + QVERIFY(menuClicks > 1); + timer.stop(); } void FlameGraphViewTest::cleanupTestCase() diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index 601f153582..a8554be3ec 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -61,6 +61,7 @@ QmlProject::QmlProject(const Utils::FileName &fileName) : setId("QmlProjectManager.QmlProject"); setProjectContext(Context(QmlProjectManager::Constants::PROJECTCONTEXT)); setProjectLanguages(Context(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID)); + setDisplayName(fileName.toFileInfo().completeBaseName()); } QmlProject::~QmlProject() @@ -239,11 +240,6 @@ void QmlProject::refreshFiles(const QSet<QString> &/*added*/, const QSet<QString } } -QString QmlProject::displayName() const -{ - return projectFilePath().toFileInfo().completeBaseName(); -} - bool QmlProject::supportsKit(Kit *k, QString *errorMessage) const { Id deviceType = DeviceTypeKitInformation::deviceTypeId(k); diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h index 616c2c6d18..75db80a866 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.h +++ b/src/plugins/qmlprojectmanager/qmlproject.h @@ -46,8 +46,6 @@ public: explicit QmlProject(const Utils::FileName &filename); ~QmlProject() override; - QString displayName() const override; - bool supportsKit(ProjectExplorer::Kit *k, QString *errorMessage) const override; Internal::QmlProjectNode *rootProjectNode() const override; diff --git a/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp b/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp index c3e1034baf..ecf191a02f 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectnodes.cpp @@ -57,15 +57,17 @@ bool QmlProjectNode::showInSimpleTree() const return true; } -QList<ProjectAction> QmlProjectNode::supportedActions(Node *node) const +bool QmlProjectNode::supportsAction(ProjectAction action, Node *node) const { - QList<ProjectAction> actions = {AddNewFile, EraseFile}; - if (node->nodeType() == NodeType::File) { + if (action == AddNewFile || action == EraseFile) + return true; + + if (action == Rename && node->nodeType() == NodeType::File) { FileNode *fileNode = static_cast<FileNode *>(node); - if (fileNode->fileType() != FileType::Project) - actions.append(Rename); + return fileNode->fileType() != FileType::Project; } - return actions; + + return false; } bool QmlProjectNode::addFiles(const QStringList &filePaths, QStringList * /*notAdded*/) diff --git a/src/plugins/qmlprojectmanager/qmlprojectnodes.h b/src/plugins/qmlprojectmanager/qmlprojectnodes.h index 3df7d7e3be..bc715d86f6 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectnodes.h +++ b/src/plugins/qmlprojectmanager/qmlprojectnodes.h @@ -38,13 +38,11 @@ class QmlProjectNode : public ProjectExplorer::ProjectNode public: QmlProjectNode(QmlProject *project); - virtual bool showInSimpleTree() const override; - - virtual QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const override; - - virtual bool addFiles(const QStringList &filePaths, QStringList *notAdded = 0) override; - virtual bool deleteFiles(const QStringList &filePaths) override; - virtual bool renameFile(const QString &filePath, const QString &newFilePath) override; + bool showInSimpleTree() const override; + bool supportsAction(ProjectExplorer::ProjectAction action, Node *node) const override; + bool addFiles(const QStringList &filePaths, QStringList *notAdded = 0) override; + bool deleteFiles(const QStringList &filePaths) override; + bool renameFile(const QString &filePath, const QString &newFilePath) override; private: QmlProject *m_project; diff --git a/src/plugins/qnx/qnxconfiguration.cpp b/src/plugins/qnx/qnxconfiguration.cpp index 295b942c4f..22718c6385 100644 --- a/src/plugins/qnx/qnxconfiguration.cpp +++ b/src/plugins/qnx/qnxconfiguration.cpp @@ -276,6 +276,7 @@ QnxToolChain *QnxConfiguration::createToolChain(const Target &target) .arg(displayName()) .arg(target.shortDescription())); toolChain->setSdpPath(sdpPath().toString()); + toolChain->setCpuDir(target.cpuDir()); toolChain->resetToolChain(qccCompilerPath()); ToolChainManager::registerToolChain(toolChain); return toolChain; diff --git a/src/plugins/qnx/qnxtoolchain.cpp b/src/plugins/qnx/qnxtoolchain.cpp index 1ae34f2a7a..99a90c89a9 100644 --- a/src/plugins/qnx/qnxtoolchain.cpp +++ b/src/plugins/qnx/qnxtoolchain.cpp @@ -42,6 +42,7 @@ namespace Qnx { namespace Internal { static const char CompilerSdpPath[] = "Qnx.QnxToolChain.NDKPath"; +static const char CpuDirKey[] = "Qnx.QnxToolChain.CpuDir"; static QList<Abi> detectTargetAbis(const FileName &sdpPath) { @@ -146,6 +147,7 @@ QVariantMap QnxToolChain::toMap() const { QVariantMap data = GccToolChain::toMap(); data.insert(QLatin1String(CompilerSdpPath), m_sdpPath); + data.insert(QLatin1String(CpuDirKey), m_cpuDir); return data; } @@ -155,6 +157,7 @@ bool QnxToolChain::fromMap(const QVariantMap &data) return false; m_sdpPath = data.value(QLatin1String(CompilerSdpPath)).toString(); + m_cpuDir = data.value(QLatin1String(CpuDirKey)).toString(); // Make the ABIs QNX specific (if they aren't already). setSupportedAbis(QnxUtils::convertAbis(supportedAbis())); @@ -176,11 +179,34 @@ void QnxToolChain::setSdpPath(const QString &sdpPath) toolChainUpdated(); } +QString QnxToolChain::cpuDir() const +{ + return m_cpuDir; +} + +void QnxToolChain::setCpuDir(const QString &cpuDir) +{ + if (m_cpuDir == cpuDir) + return; + m_cpuDir = cpuDir; + toolChainUpdated(); +} + GccToolChain::DetectedAbisResult QnxToolChain::detectSupportedAbis() const { return detectTargetAbis(FileName::fromString(m_sdpPath)); } +bool QnxToolChain::operator ==(const ToolChain &other) const +{ + if (!GccToolChain::operator ==(other)) + return false; + + auto qnxTc = static_cast<const QnxToolChain *>(&other); + + return m_sdpPath == qnxTc->m_sdpPath && m_cpuDir == qnxTc->m_cpuDir; +} + // -------------------------------------------------------------------------- // QnxToolChainFactory // -------------------------------------------------------------------------- diff --git a/src/plugins/qnx/qnxtoolchain.h b/src/plugins/qnx/qnxtoolchain.h index 93f2c15bc6..1d930a80d3 100644 --- a/src/plugins/qnx/qnxtoolchain.h +++ b/src/plugins/qnx/qnxtoolchain.h @@ -49,12 +49,17 @@ public: QString sdpPath() const; void setSdpPath(const QString &sdpPath); + QString cpuDir() const; + void setCpuDir(const QString &cpuDir); + + bool operator ==(const ToolChain &) const override; protected: virtual DetectedAbisResult detectSupportedAbis() const override; private: QString m_sdpPath; + QString m_cpuDir; }; // -------------------------------------------------------------------------- diff --git a/src/plugins/resourceeditor/resourceeditorplugin.cpp b/src/plugins/resourceeditor/resourceeditorplugin.cpp index 474092d350..1ae83161ca 100644 --- a/src/plugins/resourceeditor/resourceeditorplugin.cpp +++ b/src/plugins/resourceeditor/resourceeditorplugin.cpp @@ -338,8 +338,8 @@ void ResourceEditorPlugin::updateContextActions() if (isResourceNode) { FolderNode *parent = node ? node->parentFolderNode() : 0; - enableRename = parent && parent->supportedActions(node).contains(Rename); - enableRemove = parent && parent->supportedActions(node).contains(RemoveFile); + enableRename = parent && parent->supportsAction(Rename, node); + enableRemove = parent && parent->supportsAction(RemoveFile, node); } m_renameResourceFile->setEnabled(isResourceNode && enableRename); diff --git a/src/plugins/resourceeditor/resourcenode.cpp b/src/plugins/resourceeditor/resourcenode.cpp index 49c640840a..ffe74eeb42 100644 --- a/src/plugins/resourceeditor/resourcenode.cpp +++ b/src/plugins/resourceeditor/resourcenode.cpp @@ -160,7 +160,7 @@ public: ResourceTopLevelNode *topLevel, ResourceFolderNode *prefixNode); QString displayName() const final; - QList<ProjectAction> supportedActions(Node *node) const final; + bool supportsAction(ProjectAction, Node *node) const final; bool addFiles(const QStringList &filePaths, QStringList *notAdded) final; bool removeFiles(const QStringList &filePaths, QStringList *notRemoved) final; bool renameFile(const QString &filePath, const QString &newFilePath) final; @@ -199,17 +199,15 @@ SimpleResourceFolderNode::SimpleResourceFolderNode(const QString &afolderName, c } -QList<ProjectAction> SimpleResourceFolderNode::supportedActions(Node *) const +bool SimpleResourceFolderNode::supportsAction(ProjectAction action, Node *) const { - return { - AddNewFile, - AddExistingFile, - AddExistingDirectory, - RemoveFile, - DuplicateFile, - Rename, // Note: only works for the filename, works akwardly for relative file paths - InheritedFromParent // Do not add to list of projects when adding new file - }; + return action == AddNewFile + || action == AddExistingFile + || action == AddExistingDirectory + || action == RemoveFile + || action == DuplicateFile + || action == Rename // Note: only works for the filename, works akwardly for relative file paths + || action == InheritedFromParent; // Do not add to list of projects when adding new file } bool SimpleResourceFolderNode::addFiles(const QStringList &filePaths, QStringList *notAdded) @@ -387,11 +385,15 @@ QString ResourceTopLevelNode::addFileFilter() const return QLatin1String("*.png; *.jpg; *.gif; *.svg; *.ico; *.qml; *.qml.ui"); } -QList<ProjectAction> ResourceTopLevelNode::supportedActions(Node *node) const +bool ResourceTopLevelNode::supportsAction(ProjectAction action, Node *node) const { if (node != this) - return {}; - return {AddNewFile, AddExistingFile, AddExistingDirectory, HidePathActions, Rename}; + return false; + return action == AddNewFile + || action == AddExistingFile + || action == AddExistingDirectory + || action == HidePathActions + || action == Rename; } bool ResourceTopLevelNode::addFiles(const QStringList &filePaths, QStringList *notAdded) @@ -504,25 +506,23 @@ ResourceFolderNode::~ResourceFolderNode() } -QList<ProjectAction> ResourceFolderNode::supportedActions(Node *node) const +bool ResourceFolderNode::supportsAction(ProjectAction action, Node *node) const { Q_UNUSED(node) - QList<ProjectAction> actions = { - AddNewFile, - AddExistingFile, - AddExistingDirectory, - RemoveFile, - DuplicateFile, - Rename, // Note: only works for the filename, works akwardly for relative file paths - HidePathActions, // hides open terminal etc. - }; - // if the prefix is '/' (without lang) hide this node in add new dialog, - // as the ResouceTopLevelNode is always shown for the '/' prefix - if (m_prefix == QLatin1String("/") && m_lang.isEmpty()) - actions << InheritedFromParent; + if (action == InheritedFromParent) { + // if the prefix is '/' (without lang) hide this node in add new dialog, + // as the ResouceTopLevelNode is always shown for the '/' prefix + return m_prefix == QLatin1String("/") && m_lang.isEmpty(); + } - return actions; + return action == AddNewFile + || action == AddExistingFile + || action == AddExistingDirectory + || action == RemoveFile + || action == DuplicateFile + || action == Rename // Note: only works for the filename, works akwardly for relative file paths + || action == HidePathActions; // hides open terminal etc. } bool ResourceFolderNode::addFiles(const QStringList &filePaths, QStringList *notAdded) @@ -674,12 +674,11 @@ QString ResourceFileNode::qrcPath() const return m_qrcPath; } -QList<ProjectAction> ResourceFileNode::supportedActions(Node *node) const +bool ResourceFileNode::supportsAction(ProjectAction action, Node *node) const { - QList<ProjectAction> actions = parentFolderNode()->supportedActions(node); - actions.removeOne(HidePathActions); - return actions; + if (action == HidePathActions) + return false; + return parentFolderNode()->supportsAction(action, node); } - } // ResourceEditor diff --git a/src/plugins/resourceeditor/resourcenode.h b/src/plugins/resourceeditor/resourcenode.h index aa7ff02580..0d6b7a1b64 100644 --- a/src/plugins/resourceeditor/resourcenode.h +++ b/src/plugins/resourceeditor/resourcenode.h @@ -41,7 +41,7 @@ public: QString addFileFilter() const override; - QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const override; + bool supportsAction(ProjectExplorer::ProjectAction action, Node *node) const override; bool addFiles(const QStringList &filePaths, QStringList *notAdded) override; bool removeFiles(const QStringList &filePaths, QStringList *notRemoved) override; @@ -67,7 +67,7 @@ public: ResourceFolderNode(const QString &prefix, const QString &lang, ResourceTopLevelNode *parent); ~ResourceFolderNode() override; - QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const override; + bool supportsAction(ProjectExplorer::ProjectAction action, Node *node) const override; QString displayName() const override; @@ -97,7 +97,7 @@ public: QString displayName() const override; QString qrcPath() const; - QList<ProjectExplorer::ProjectAction> supportedActions(Node *node) const override; + bool supportsAction(ProjectExplorer::ProjectAction action, Node *node) const override; private: QString m_qrcPath; diff --git a/src/plugins/scxmleditor/common/colorthemes.cpp b/src/plugins/scxmleditor/common/colorthemes.cpp index 821e98e123..fc06f03584 100644 --- a/src/plugins/scxmleditor/common/colorthemes.cpp +++ b/src/plugins/scxmleditor/common/colorthemes.cpp @@ -98,7 +98,7 @@ void ColorThemes::updateColorThemeMenu() for (const QString &key: keys) { const QString actionText = key == Constants::C_COLOR_SCHEME_DEFAULT ? tr("Factory Default") : key == Constants::C_COLOR_SCHEME_SCXMLDOCUMENT - ? tr("Colors from SCXML-document") + ? tr("Colors from SCXML Document") : key; QAction *action = m_menu->addAction(actionText, this, [this, key]() { selectColorTheme(key); diff --git a/src/plugins/scxmleditor/plugin_interface/idwarningitem.cpp b/src/plugins/scxmleditor/plugin_interface/idwarningitem.cpp index 8752196405..2e50ac5b64 100644 --- a/src/plugins/scxmleditor/plugin_interface/idwarningitem.cpp +++ b/src/plugins/scxmleditor/plugin_interface/idwarningitem.cpp @@ -33,7 +33,7 @@ IdWarningItem::IdWarningItem(QGraphicsItem *parent) { setSeverity(OutputPane::Warning::ErrorType); setTypeName(tr("State")); - setDescription(tr("Each State has to be unique ID.")); + setDescription(tr("Each state must have a unique ID.")); setReason(tr("Missing ID")); setX(-boundingRect().width()); } diff --git a/src/plugins/scxmleditor/plugin_interface/initialwarningitem.cpp b/src/plugins/scxmleditor/plugin_interface/initialwarningitem.cpp index 5b15f1d8bf..987113eca0 100644 --- a/src/plugins/scxmleditor/plugin_interface/initialwarningitem.cpp +++ b/src/plugins/scxmleditor/plugin_interface/initialwarningitem.cpp @@ -35,8 +35,8 @@ InitialWarningItem::InitialWarningItem(InitialStateItem *parent) { setSeverity(OutputPane::Warning::ErrorType); setTypeName(tr("Initial")); - setDescription(tr("It is possible to have max 1 initial-state in the same level.")); - setReason(tr("Too many initial states in the same level")); + setDescription(tr("One level can contain only one initial state.")); + setReason(tr("Too many initial states at the same level")); } void InitialWarningItem::updatePos() diff --git a/src/plugins/scxmleditor/plugin_interface/scxmldocument.cpp b/src/plugins/scxmleditor/plugin_interface/scxmldocument.cpp index ed4ce3e507..4b7d61175c 100644 --- a/src/plugins/scxmleditor/plugin_interface/scxmldocument.cpp +++ b/src/plugins/scxmleditor/plugin_interface/scxmldocument.cpp @@ -262,16 +262,16 @@ void ScxmlDocument::initErrorMessage(const QXmlStreamReader &xml, QIODevice *io) QString errorString; switch (xml.error()) { case QXmlStreamReader::Error::UnexpectedElementError: - errorString = tr("Unexpected element"); + errorString = tr("Unexpected element."); break; case QXmlStreamReader::Error::NotWellFormedError: - errorString = tr("Not well formed"); + errorString = tr("Not well formed."); break; case QXmlStreamReader::Error::PrematureEndOfDocumentError: - errorString = tr("Premature end of document"); + errorString = tr("Premature end of document."); break; case QXmlStreamReader::Error::CustomError: - errorString = tr("Custom error"); + errorString = tr("Custom error."); break; default: break; @@ -299,7 +299,7 @@ bool ScxmlDocument::pasteData(const QByteArray &data, const QPointF &minPos, con if (!m_currentTag) { m_hasError = true; - m_lastError = tr("Current tag not selected"); + m_lastError = tr("Current tag is not selected."); return false; } @@ -310,7 +310,7 @@ bool ScxmlDocument::pasteData(const QByteArray &data, const QPointF &minPos, con } bool ok = true; - m_undoStack->beginMacro(tr("Paste item(s)")); + m_undoStack->beginMacro(tr("Paste items")); QByteArray d(data); QBuffer buffer(&d); @@ -458,7 +458,7 @@ bool ScxmlDocument::save(const QString &fileName) } file.close(); if (!ok) - m_lastError = tr("Cannot save xml to the file %1.").arg(fileName); + m_lastError = tr("Cannot save XML to the file %1.").arg(fileName); } else { ok = false; m_lastError = tr("Cannot open file %1.").arg(fileName); diff --git a/src/plugins/scxmleditor/plugin_interface/stateitem.cpp b/src/plugins/scxmleditor/plugin_interface/stateitem.cpp index 3f88a25ec0..532851632c 100644 --- a/src/plugins/scxmleditor/plugin_interface/stateitem.cpp +++ b/src/plugins/scxmleditor/plugin_interface/stateitem.cpp @@ -329,7 +329,7 @@ void StateItem::selectedMenuAction(const QAction *action) case TagUtils::SetAsInitial: { ScxmlTag *parentTag = tag->parentTag(); if (parentTag) { - document->undoStack()->beginMacro(tr("Change initial-state")); + document->undoStack()->beginMacro(tr("Change initial state")); ScxmlTag *initialTag = parentTag->child("initial"); if (initialTag) { diff --git a/src/plugins/scxmleditor/scxmleditorconstants.h b/src/plugins/scxmleditor/scxmleditorconstants.h index 8dd3bc360e..c503ab5f48 100644 --- a/src/plugins/scxmleditor/scxmleditorconstants.h +++ b/src/plugins/scxmleditor/scxmleditorconstants.h @@ -33,7 +33,7 @@ namespace Constants { const char INFO_READ_ONLY[] = "ScxmlEditor.ReadOnly"; const char C_SCXMLEDITOR[] = "Qt5.ScxmlEditor"; -const char C_SCXMLEDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("OpenWith::Editors", "Scxml Editor"); +const char C_SCXMLEDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("OpenWith::Editors", "SCXML Editor"); const char K_SCXML_EDITOR_ID[] = "ScxmlEditor.XmlEditor"; const char C_SCXML_EDITOR[] = "Scxml Editor"; diff --git a/src/plugins/texteditor/circularclipboardassist.cpp b/src/plugins/texteditor/circularclipboardassist.cpp index 6f3e4e4349..0941a83bfe 100644 --- a/src/plugins/texteditor/circularclipboardassist.cpp +++ b/src/plugins/texteditor/circularclipboardassist.cpp @@ -94,6 +94,7 @@ public: QIcon icon = QIcon::fromTheme(QLatin1String("edit-paste"), Utils::Icons::PASTE.icon()).pixmap(16); CircularClipboard * clipboard = CircularClipboard::instance(); QList<AssistProposalItemInterface *> items; + items.reserve(clipboard->size()); for (int i = 0; i < clipboard->size(); ++i) { QSharedPointer<const QMimeData> data = clipboard->next(); diff --git a/src/plugins/texteditor/textdocumentlayout.cpp b/src/plugins/texteditor/textdocumentlayout.cpp index f413b5c363..df6a82495d 100644 --- a/src/plugins/texteditor/textdocumentlayout.cpp +++ b/src/plugins/texteditor/textdocumentlayout.cpp @@ -527,7 +527,7 @@ void TextDocumentLayout::setFolded(const QTextBlock &block, bool folded) if (folded) userData(block)->setFolded(true); else if (TextBlockUserData *userData = testUserData(block)) - return userData->setFolded(false); + userData->setFolded(false); } void TextDocumentLayout::requestExtraAreaUpdate() diff --git a/src/plugins/valgrind/callgrind/callgrindproxymodel.cpp b/src/plugins/valgrind/callgrind/callgrindproxymodel.cpp index 44df8fbbc8..c1ac384062 100644 --- a/src/plugins/valgrind/callgrind/callgrindproxymodel.cpp +++ b/src/plugins/valgrind/callgrind/callgrindproxymodel.cpp @@ -121,14 +121,17 @@ bool DataProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_ const Function *func = source_index.data(DataModel::FunctionRole).value<const Function *>(); + if (!func) + return false; + // check if func is located in the specific base directory, if any - if (func && !m_baseDir.isEmpty()) { + if (!m_baseDir.isEmpty()) { if (!func->location().startsWith(m_baseDir)) return false; } // check if the function from this index is a child of (called by) the filter function - if (func && m_function) { + if (m_function) { bool isValid = false; foreach (const FunctionCall *call, func->incomingCalls()) { if (call->caller() == m_function) { diff --git a/src/shared/qbs b/src/shared/qbs -Subproject 28f803d359c5f3102514d7817478cb492711410 +Subproject cc99f7de4e1578feb1ca8e5a2ca7384b25b6d42 |