diff --git a/doc/api/qtcreator-documentation.qdoc b/doc/api/qtcreator-documentation.qdoc index 8d53f03bbec..935f6d10690 100644 --- a/doc/api/qtcreator-documentation.qdoc +++ b/doc/api/qtcreator-documentation.qdoc @@ -195,6 +195,31 @@ save space. \endlist + \section2 Hightlighting Parts of the Screen + + You can use number icons in screenshots to highlight parts of the screenshot + (instead of using red arrows or borders, or something similar). You can then + refer to the numbers in text. For and example, see the + \l{http://doc.qt.io/qt-5/topics-app-development.html}{Development Tools} + topic in the Qt reference documentation. + + This improves the consistency of the look and feel of Qt documentation, + and eliminates the need to describe parts of the UI in the text, because + you can just insert the number of the element you are referring to in + brackets. + + You can find a set of images that show the numbers from 1 to 10 in the + \c doc\images\numbers directory (or in the \c qtdoc module sources in + \c doc\images\numbers). + + To use the numbers: + + \list + \li Take a screenshot as described above. + \li After resizing the screenshot, copy-paste the number images on + the screenshot to the places that you want to refer to from text. + \endlist + \section2 Optimizing Images Save images in the PNG format in the \QC project folder in the @@ -241,6 +266,18 @@ You can also see the sizes of the initial and optimized image. + \section3 Using OptiPNG + + Download and install \l{https://sourceforge.net/projects/optipng/}{OptiPNG}. + + OptiPNG is a command-line tool that you can invoke from the \QC project + folder (or any folder that contains your project). To optimize a screenshot, + enter the following command (here, from the \QC project folder): + + \code + optipng -o 7 -strip all doc/images/ + \endcode + \section1 Building Documentation You use QDoc to build the documentation. Build the documentation from time diff --git a/src/libs/utils/stringutils.cpp b/src/libs/utils/stringutils.cpp index f2a13991cb2..0bca020553e 100644 --- a/src/libs/utils/stringutils.cpp +++ b/src/libs/utils/stringutils.cpp @@ -269,4 +269,71 @@ QTCREATOR_UTILS_EXPORT bool readMultiLineString(const QJsonValue &value, QString return true; } +QTCREATOR_UTILS_EXPORT int parseUsedPortFromNetstatOutput(const QByteArray &line) +{ + const QByteArray trimmed = line.trimmed(); + int base = 0; + QByteArray portString; + + if (trimmed.startsWith("TCP") || trimmed.startsWith("UDP")) { + // Windows. Expected output is something like + // + // Active Connections + // + // Proto Local Address Foreign Address State + // TCP 0.0.0.0:80 0.0.0.0:0 LISTENING + // TCP 0.0.0.0:113 0.0.0.0:0 LISTENING + // [...] + // TCP 10.9.78.4:14714 0.0.0.0:0 LISTENING + // TCP 10.9.78.4:50233 12.13.135.180:993 ESTABLISHED + // [...] + // TCP [::]:445 [::]:0 LISTENING + // TCP 192.168.0.80:51905 169.55.74.50:443 ESTABLISHED + // UDP [fe80::880a:2932:8dff:a858%6]:1900 *:* + const int firstBracketPos = trimmed.indexOf('['); + int colonPos = -1; + if (firstBracketPos == -1) { + colonPos = trimmed.indexOf(':'); // IPv4 + } else { + // jump over host part + const int secondBracketPos = trimmed.indexOf(']', firstBracketPos + 1); + colonPos = trimmed.indexOf(':', secondBracketPos); + } + const int firstDigitPos = colonPos + 1; + const int spacePos = trimmed.indexOf(' ', firstDigitPos); + if (spacePos < 0) + return -1; + const int len = spacePos - firstDigitPos; + base = 10; + portString = trimmed.mid(firstDigitPos, len); + } else { + // Expected output on Linux something like + // + // sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt ... + // 0: 00000000:2805 00000000:0000 0A 00000000:00000000 00:00000000 00000000 ... + // + const int firstColonPos = trimmed.indexOf(':'); + if (firstColonPos < 0) + return -1; + const int secondColonPos = trimmed.indexOf(':', firstColonPos + 1); + if (secondColonPos < 0) + return -1; + const int spacePos = trimmed.indexOf(' ', secondColonPos + 1); + if (spacePos < 0) + return -1; + const int len = spacePos - secondColonPos - 1; + base = 16; + portString = trimmed.mid(secondColonPos + 1, len); + } + + bool ok = true; + const int port = portString.toInt(&ok, base); + if (!ok) { + qWarning("%s: Unexpected string '%s' is not a port. Tried to read from '%s'", + Q_FUNC_INFO, line.data(), portString.data()); + return -1; + } + return port; +} + } // namespace Utils diff --git a/src/libs/utils/stringutils.h b/src/libs/utils/stringutils.h index 0723e76656f..1ec3a0aeecc 100644 --- a/src/libs/utils/stringutils.h +++ b/src/libs/utils/stringutils.h @@ -79,4 +79,6 @@ private: QTCREATOR_UTILS_EXPORT void expandMacros(QString *str, AbstractMacroExpander *mx); QTCREATOR_UTILS_EXPORT QString expandMacros(const QString &str, AbstractMacroExpander *mx); +QTCREATOR_UTILS_EXPORT int parseUsedPortFromNetstatOutput(const QByteArray &line); + } // namespace Utils diff --git a/src/plugins/baremetal/openocdgdbserverprovider.cpp b/src/plugins/baremetal/openocdgdbserverprovider.cpp index 91b869f94b0..cca6d9964f9 100644 --- a/src/plugins/baremetal/openocdgdbserverprovider.cpp +++ b/src/plugins/baremetal/openocdgdbserverprovider.cpp @@ -164,7 +164,7 @@ bool OpenOcdGdbServerProvider::isValid() const } if (m == StartupOnNetwork || m == StartupOnPipe) { - if (m_executableFile.isEmpty() || m_configurationFile.isEmpty()) + if (m_executableFile.isEmpty()) return false; } diff --git a/src/plugins/cppeditor/cppuseselectionsupdater.cpp b/src/plugins/cppeditor/cppuseselectionsupdater.cpp index cf988427298..ac071a02b01 100644 --- a/src/plugins/cppeditor/cppuseselectionsupdater.cpp +++ b/src/plugins/cppeditor/cppuseselectionsupdater.cpp @@ -98,6 +98,8 @@ void CppUseSelectionsUpdater::update(CallType callType) const int startRevision = cppEditorDocument->document()->revision(); QFuture future = cppEditorDocument->cursorInfo(params); + if (future.isCanceled()) + return; // QFuture::waitForFinished seems to block completely, not even // allowing to process events from QLocalSocket. diff --git a/src/plugins/cpptools/builtincursorinfo.cpp b/src/plugins/cpptools/builtincursorinfo.cpp index 6cdf19e5fef..8902fa05ca9 100644 --- a/src/plugins/cpptools/builtincursorinfo.cpp +++ b/src/plugins/cpptools/builtincursorinfo.cpp @@ -358,6 +358,9 @@ QFuture BuiltinCursorInfo::run(const CursorInfoParams &cursorInfoPar CppTools::SemanticInfo::LocalUseMap BuiltinCursorInfo::findLocalUses(const Document::Ptr &document, int line, int column) { + if (!document || !document->translationUnit() || !document->translationUnit()->ast()) + return SemanticInfo::LocalUseMap(); + AST *ast = document->translationUnit()->ast(); FunctionDefinitionUnderCursor functionDefinitionUnderCursor(document->translationUnit()); DeclarationAST *declaration = functionDefinitionUnderCursor(ast, diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index d64fd67ebda..091acae9340 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -2490,6 +2490,8 @@ void DebuggerPluginPrivate::cleanupViews() bool keepIt = true; if (document->isModified()) keepIt = true; + else if (document->filePath().toString().contains("qeventdispatcher")) + keepIt = false; else if (isMemory) keepIt = !closeMemory; else diff --git a/src/plugins/help/helpviewer.cpp b/src/plugins/help/helpviewer.cpp index ebd4ae44c08..7a553000b23 100644 --- a/src/plugins/help/helpviewer.cpp +++ b/src/plugins/help/helpviewer.cpp @@ -85,6 +85,11 @@ HelpViewer::HelpViewer(QWidget *parent) { } +HelpViewer::~HelpViewer() +{ + restoreOverrideCursor(); +} + void HelpViewer::setActionVisible(Action action, bool visible) { if (visible) @@ -156,16 +161,25 @@ void HelpViewer::home() void HelpViewer::slotLoadStarted() { + ++m_loadOverrideStack; QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); } void HelpViewer::slotLoadFinished() { - QGuiApplication::restoreOverrideCursor(); + restoreOverrideCursor(); emit sourceChanged(source()); emit loadFinished(); } +void HelpViewer::restoreOverrideCursor() +{ + while (m_loadOverrideStack > 0) { + --m_loadOverrideStack; + QGuiApplication::restoreOverrideCursor(); + } +} + bool HelpViewer::handleForwardBackwardMouseButtons(QMouseEvent *event) { if (event->button() == Qt::XButton1) { diff --git a/src/plugins/help/helpviewer.h b/src/plugins/help/helpviewer.h index 488bb0bc5e2..c98e8dbbfb4 100644 --- a/src/plugins/help/helpviewer.h +++ b/src/plugins/help/helpviewer.h @@ -49,7 +49,7 @@ public: Q_DECLARE_FLAGS(Actions, Action) explicit HelpViewer(QWidget *parent = 0); - ~HelpViewer() { } + ~HelpViewer(); virtual QFont viewerFont() const = 0; virtual void setViewerFont(const QFont &font) = 0; @@ -109,7 +109,10 @@ protected: void slotLoadStarted(); void slotLoadFinished(); + void restoreOverrideCursor(); + Actions m_visibleActions = 0; + int m_loadOverrideStack = 0; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/compileoutputwindow.cpp b/src/plugins/projectexplorer/compileoutputwindow.cpp index 7f15decb549..2f1e4f75a0c 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.cpp +++ b/src/plugins/projectexplorer/compileoutputwindow.cpp @@ -110,8 +110,8 @@ private: protected: void mouseMoveEvent(QMouseEvent *ev) { - int line = cursorForPosition(ev->pos()).block().blockNumber(); - if (m_taskids.value(line, 0)) + const int line = cursorForPosition(ev->pos()).block().blockNumber(); + if (m_taskids.contains(line) && m_mousePressButton == Qt::NoButton) viewport()->setCursor(Qt::PointingHandCursor); else viewport()->setCursor(Qt::IBeamCursor); @@ -121,23 +121,27 @@ protected: void mousePressEvent(QMouseEvent *ev) { m_mousePressPosition = ev->pos(); + m_mousePressButton = ev->button(); QPlainTextEdit::mousePressEvent(ev); } void mouseReleaseEvent(QMouseEvent *ev) { - if ((m_mousePressPosition - ev->pos()).manhattanLength() < 4) { + if ((m_mousePressPosition - ev->pos()).manhattanLength() < 4 + && m_mousePressButton == Qt::LeftButton) { int line = cursorForPosition(ev->pos()).block().blockNumber(); if (unsigned taskid = m_taskids.value(line, 0)) TaskHub::showTaskInEditor(taskid); } + m_mousePressButton = Qt::NoButton; QPlainTextEdit::mouseReleaseEvent(ev); } private: QHash m_taskids; //Map blocknumber to taskId QPoint m_mousePressPosition; + Qt::MouseButton m_mousePressButton = Qt::NoButton; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp index 7f243e8e435..7e81057c634 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/desktopdevice.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include @@ -168,65 +169,10 @@ class DesktopPortsGatheringMethod : public PortsGatheringMethod { QList ports; const QList lines = output.split('\n'); - if (HostOsInfo::isWindowsHost()) { - // Expected output is something like - // - // Active Connections - // - // Proto Local Address Foreign Address State - // TCP 0.0.0.0:80 0.0.0.0:0 LISTENING - // TCP 0.0.0.0:113 0.0.0.0:0 LISTENING - // [...] - // TCP 10.9.78.4:14714 0.0.0.0:0 LISTENING - // TCP 10.9.78.4:50233 12.13.135.180:993 ESTABLISHED - for (const QByteArray &line : lines) { - const QByteArray trimmed = line.trimmed(); - if (!trimmed.startsWith("TCP")) - continue; - int colonPos = trimmed.indexOf(':'); - if (colonPos < 0) - continue; - int spacePos = trimmed.indexOf(':', colonPos + 1); - if (spacePos < 0) - continue; - bool ok; - int len = spacePos - colonPos - 1; - const Utils::Port port(line.mid(colonPos + 1, len).toInt(&ok, 16)); - if (ok) { - if (!ports.contains(port)) - ports << port; - } else { - qWarning("%s: Unexpected string '%s' is not a port.", - Q_FUNC_INFO, line.data()); - } - } - } else if (HostOsInfo::isLinuxHost()) { - // Expected outpit is something like - // - // sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt ... - // 0: 00000000:2805 00000000:0000 0A 00000000:00000000 00:00000000 00000000 ... - // - for (const QByteArray &line : lines) { - int firstColonPos = line.indexOf(':'); - if (firstColonPos < 0) - continue; - int secondColonPos = line.indexOf(':', firstColonPos + 1); - if (secondColonPos < 0) - continue; - int spacePos = line.indexOf(':', secondColonPos + 1); - if (spacePos < 0) - continue; - bool ok; - int len = spacePos - secondColonPos - 1; - const Utils::Port port(line.mid(secondColonPos + 1, len).toInt(&ok, 16)); - if (ok) { - if (!ports.contains(port)) - ports << port; - } else { - qWarning("%s: Unexpected string '%s' is not a port.", - Q_FUNC_INFO, line.data()); - } - } + for (const QByteArray &line : lines) { + const Port port(Utils::parseUsedPortFromNetstatOutput(line)); + if (port.isValid() && !ports.contains(port)) + ports.append(port); } return ports; } diff --git a/src/tools/clangbackend/ipcsource/clangdocument.cpp b/src/tools/clangbackend/ipcsource/clangdocument.cpp index a4f95d17b84..cbbaf51b332 100644 --- a/src/tools/clangbackend/ipcsource/clangdocument.cpp +++ b/src/tools/clangbackend/ipcsource/clangdocument.cpp @@ -150,6 +150,11 @@ bool Document::isIntact() const && !d->hasParseOrReparseFailed; } +bool Document::isParsed() const +{ + return d->translationUnits.areAllTranslationUnitsParsed(); +} + Utf8String Document::filePath() const { checkIfNull(); diff --git a/src/tools/clangbackend/ipcsource/clangdocument.h b/src/tools/clangbackend/ipcsource/clangdocument.h index 260705c1354..415deefbe9d 100644 --- a/src/tools/clangbackend/ipcsource/clangdocument.h +++ b/src/tools/clangbackend/ipcsource/clangdocument.h @@ -77,6 +77,7 @@ public: bool isNull() const; bool isIntact() const; + bool isParsed() const; Utf8String filePath() const; Utf8StringVector fileArguments() const; diff --git a/src/tools/clangbackend/ipcsource/clangdocumentsuspenderresumer.cpp b/src/tools/clangbackend/ipcsource/clangdocumentsuspenderresumer.cpp index 8edde48fd62..bae98893187 100644 --- a/src/tools/clangbackend/ipcsource/clangdocumentsuspenderresumer.cpp +++ b/src/tools/clangbackend/ipcsource/clangdocumentsuspenderresumer.cpp @@ -97,7 +97,8 @@ static bool isSuspendable(const Document &document) { return isFineDocument(document) && !document.isSuspended() - && !document.isVisibleInEditor(); + && !document.isVisibleInEditor() + && document.isParsed(); } static bool isResumable(const Document &document) diff --git a/tests/auto/utils/stringutils/tst_stringutils.cpp b/tests/auto/utils/stringutils/tst_stringutils.cpp index 96a950bb436..5a5933b323c 100644 --- a/tests/auto/utils/stringutils/tst_stringutils.cpp +++ b/tests/auto/utils/stringutils/tst_stringutils.cpp @@ -82,6 +82,8 @@ private slots: void testMacroExpander(); void testStripAccelerator(); void testStripAccelerator_data(); + void testParseUsedPortFromNetstatOutput(); + void testParseUsedPortFromNetstatOutput_data(); private: TestMacroExpander mx; @@ -202,6 +204,37 @@ void tst_StringUtils::testStripAccelerator_data() QTest::newRow("Test&") << "Test"; } +void tst_StringUtils::testParseUsedPortFromNetstatOutput() +{ + QFETCH(QString, line); + QFETCH(int, port); + + QCOMPARE(Utils::parseUsedPortFromNetstatOutput(line.toUtf8()), port); +} + +void tst_StringUtils::testParseUsedPortFromNetstatOutput_data() +{ + QTest::addColumn("line"); + QTest::addColumn("port"); + + QTest::newRow("Empty") << "" << -1; + + // Windows netstat. + QTest::newRow("Win1") << "Active Connection" << -1; + QTest::newRow("Win2") << " Proto Local Address Foreign Address State" << -1; + QTest::newRow("Win3") << " TCP 0.0.0.0:80 0.0.0.0:0 LISTENING" << 80; + QTest::newRow("Win4") << " TCP 0.0.0.0:113 0.0.0.0:0 LISTENING" << 113; + QTest::newRow("Win5") << " TCP 10.9.78.4:14714 0.0.0.0:0 LISTENING" << 14714; + QTest::newRow("Win6") << " TCP 10.9.78.4:50233 12.13.135.180:993 ESTABLISHED" << 50233; + QTest::newRow("Win7") << " TCP [::]:445 [::]:0 LISTENING" << 445; + QTest::newRow("Win8") << " TCP 192.168.0.80:51905 169.55.74.50:443 ESTABLISHED" << 51905; + QTest::newRow("Win9") << " UDP [fe80::840a:2942:8def:abcd%6]:1900 *:* " << 1900; + + // Linux + QTest::newRow("Linux1") << "sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt ..." << -1; + QTest::newRow("Linux2") << "0: 00000000:2805 00000000:0000 0A 00000000:00000000 00:00000000 00000000 ..." << 10245; +} + QTEST_MAIN(tst_StringUtils) #include "tst_stringutils.moc" diff --git a/tests/unit/unittest/clangdocumentsuspenderresumer-test.cpp b/tests/unit/unittest/clangdocumentsuspenderresumer-test.cpp index 8ab78ebded6..823a84482ca 100644 --- a/tests/unit/unittest/clangdocumentsuspenderresumer-test.cpp +++ b/tests/unit/unittest/clangdocumentsuspenderresumer-test.cpp @@ -73,6 +73,7 @@ protected: Document getDocument(const Utf8String &filePath); void categorizeDocuments(int hotDocumentsSize); SuspendResumeJobs createSuspendResumeJobs(int hotDocumentsSize = -1); + static void setParsed(Document &document); protected: ClangBackEnd::ProjectParts projects; @@ -176,6 +177,8 @@ TEST_F(DocumentSuspenderResumer, CreateSuspendJobForInvisible) Document document = documents.create({fileContainer1})[0]; document.setIsSuspended(false); document.setIsVisibleInEditor(false, Clock::now()); + setParsed(document); + const SuspendResumeJobs expectedJobs = { {document, JobRequest::Type::SuspendDocument, PreferredTranslationUnit::RecentlyParsed} }; @@ -196,12 +199,24 @@ TEST_F(DocumentSuspenderResumer, DoNotCreateSuspendJobForVisible) ASSERT_THAT(jobs, ContainerEq(SuspendResumeJobs())); } +TEST_F(DocumentSuspenderResumer, DoNotCreateSuspendJobForUnparsed) +{ + Document document = documents.create({fileContainer1})[0]; + document.setIsSuspended(false); + document.setIsVisibleInEditor(true, Clock::now()); + + const SuspendResumeJobs jobs = createSuspendResumeJobs(/*hotDocumentsSize=*/ 0); + + ASSERT_THAT(jobs, ContainerEq(SuspendResumeJobs())); +} + TEST_F(DocumentSuspenderResumer, CreateSuspendJobsForDocumentWithSupportiveTranslationUnit) { Document document = documents.create({fileContainer1})[0]; document.setIsSuspended(false); document.setIsVisibleInEditor(false, Clock::now()); document.translationUnits().createAndAppend(); // Add supportive translation unit + setParsed(document); const SuspendResumeJobs expectedJobs = { {document, JobRequest::Type::SuspendDocument, PreferredTranslationUnit::RecentlyParsed}, {document, JobRequest::Type::SuspendDocument, PreferredTranslationUnit::PreviouslyParsed}, @@ -258,6 +273,7 @@ TEST_F(DocumentSuspenderResumer, CreateSuspendAndResumeJobs) Document hotDocument = documents.create({fileContainer1})[0]; hotDocument.setIsSuspended(true); Document coldDocument = documents.create({fileContainer2})[0]; + setParsed(coldDocument); coldDocument.setIsSuspended(false); documents.setVisibleInEditors({filePath1}); const SuspendResumeJobs expectedJobs = { @@ -292,4 +308,15 @@ DocumentSuspenderResumer::createSuspendResumeJobs(int hotDocumentsSize) return ClangBackEnd::createSuspendResumeJobs(documents.documents(), hotDocumentsSize); } +void DocumentSuspenderResumer::setParsed(ClangBackEnd::Document &document) +{ + const Utf8String first = document.translationUnit().id(); + document.translationUnits().updateParseTimePoint(first, Clock::now()); + + const Utf8String second + = document.translationUnit(PreferredTranslationUnit::LastUninitialized).id(); + if (second != first) + document.translationUnits().updateParseTimePoint(second, Clock::now()); +} + } // anonymous