diff --git a/kdevplatform/shell/tests/test_projectcontroller.cpp b/kdevplatform/shell/tests/test_projectcontroller.cpp index d32127198f..635e98a841 100644 --- a/kdevplatform/shell/tests/test_projectcontroller.cpp +++ b/kdevplatform/shell/tests/test_projectcontroller.cpp @@ -1,580 +1,580 @@ /*************************************************************************** * Copyright 2008 Manuel Breugelmans * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "test_projectcontroller.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; Q_DECLARE_METATYPE(KDevelop::IProject*) namespace { class DialogProviderFake : public IProjectDialogProvider { Q_OBJECT public: DialogProviderFake() {} ~DialogProviderFake() override {} bool m_reopen = true; public Q_SLOTS: QUrl askProjectConfigLocation(bool /*fetch*/, const QUrl& /*startUrl*/, const QUrl& /*repoUrl*/, IPlugin* /*plugin*/) override { return QUrl(); } bool userWantsReopen() override { return m_reopen; } }; } /*! A Filemanager plugin that allows you to setup a file & directory structure */ class FakeFileManager : public IPlugin, public IProjectFileManager { Q_OBJECT Q_INTERFACES(KDevelop::IProjectFileManager) public: FakeFileManager(QObject*, const QVariantList&) : IPlugin(ICore::self()->aboutData().componentName(), Core::self()) { } FakeFileManager() : IPlugin(ICore::self()->aboutData().componentName(), Core::self()) { } ~FakeFileManager() override {} Features features() const override { return IProjectFileManager::Files | IProjectFileManager::Folders; } QMap m_filesInFolder; // initialize QMap m_subFoldersInFolder; /*! Setup this manager such that @p folder contains @p file */ void addFileToFolder(const Path& folder, const Path& file) { if (!m_filesInFolder.contains(folder)) { m_filesInFolder[folder] = Path::List(); } m_filesInFolder[folder] << file; } /*! Setup this manager such that @p folder has @p subFolder */ void addSubFolderTo(const Path& folder, const Path& subFolder) { if (!m_subFoldersInFolder.contains(folder)) { m_subFoldersInFolder[folder] = Path::List(); } m_subFoldersInFolder[folder] << subFolder; } QList parse(ProjectFolderItem *dom) override { Path::List files = m_filesInFolder[dom->path()]; foreach (const Path& file, files) { new ProjectFileItem(dom->project(), file, dom); } Path::List folderPaths = m_subFoldersInFolder[dom->path()]; QList folders; foreach (const Path& folderPath, folderPaths) { folders << new ProjectFolderItem(dom->project(), folderPath, dom); } return folders; } ProjectFolderItem *import(IProject *project) override { ProjectFolderItem* it = new ProjectFolderItem(project, project->path()); return it; } - ProjectFolderItem* addFolder(const Path& /*folder*/, ProjectFolderItem */*parent*/) override { return nullptr; } - ProjectFileItem* addFile(const Path& /*file*/, ProjectFolderItem */*parent*/) override { return nullptr; } + ProjectFolderItem* addFolder(const Path& /*folder*/, ProjectFolderItem* /*parent*/) override { return nullptr; } + ProjectFileItem* addFile(const Path& /*file*/, ProjectFolderItem* /*parent*/) override { return nullptr; } bool removeFilesAndFolders(const QList &/*items*/) override { return false; } bool moveFilesAndFolders(const QList< KDevelop::ProjectBaseItem* > &/*items*/, KDevelop::ProjectFolderItem* /*newParent*/) override { return false; } bool copyFilesAndFolders(const Path::List &/*items*/, KDevelop::ProjectFolderItem* /*newParent*/) override { return false; } bool renameFile(ProjectFileItem* /*file*/, const Path& /*newPath*/) override { return false; } bool renameFolder(ProjectFolderItem* /*oldFolder*/, const Path& /*newPath*/ ) override { return false; } bool reload(ProjectFolderItem* /*item*/) override { return false; } }; class FakePluginController : public PluginController { Q_OBJECT public: using PluginController::PluginController; IPlugin* pluginForExtension(const QString& extension, const QString& pluginName = {}, const QVariantMap& constraints = QVariantMap()) override { if (extension == qobject_interface_iid()) { if (!m_fakeFileManager) { // Can't initialize in the constructor, because the pluginController must be setup // before constructing a plugin, and this _is_ the pluginController. m_fakeFileManager = new FakeFileManager; } return m_fakeFileManager; } return PluginController::pluginForExtension(extension, pluginName, constraints); } private: FakeFileManager* m_fakeFileManager = nullptr; }; ////////////////////// Fixture /////////////////////////////////////////////// void TestProjectController::initTestCase() { AutoTestShell::init({{}}); TestCore* testCore = new TestCore; testCore->setPluginController( new FakePluginController(testCore) ); testCore->initialize(); qRegisterMetaType(); m_core = Core::self(); m_scratchDir = QDir(QDir::tempPath()); m_scratchDir.mkdir(QStringLiteral("prjctrltest")); m_scratchDir.cd(QStringLiteral("prjctrltest")); QSignalSpy projectControllerInitializedSpy(m_core->projectControllerInternal(), &ProjectController::initialized); QVERIFY(projectControllerInitializedSpy.wait(100)); } void TestProjectController::cleanupTestCase() { TestCore::shutdown(); } void TestProjectController::init() { m_projName = QStringLiteral("foo"); m_projFilePath = writeProjectConfig(m_projName); m_projCtrl = m_core->projectControllerInternal(); m_tmpConfigs << m_projFilePath; m_projFolder = Path(m_scratchDir.absolutePath() + '/'); } void TestProjectController::cleanup() { // also close any opened projects as we do not get a clean fixture, // following tests should start off clean. foreach(IProject* p, m_projCtrl->projects()) { m_projCtrl->closeProject(p); } foreach(const Path &cfg, m_tmpConfigs) { QFile::remove(cfg.pathOrUrl()); } qDeleteAll(m_fileManagerGarbage); m_fileManagerGarbage.clear(); } ////////////////////// Commands ////////////////////////////////////////////// #define WAIT_FOR_OPEN_SIGNAL \ {\ QSignalSpy signal(m_projCtrl, SIGNAL(projectOpened(KDevelop::IProject*)));\ QVERIFY2(signal.wait(30000), "Timeout while waiting for opened signal");\ } void(0) void TestProjectController::openProject() { QSignalSpy* spy = createOpenedSpy(); QVERIFY(!m_projCtrl->isProjectNameUsed(m_projName)); m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; QCOMPARE(m_projCtrl->projectCount(), 1); IProject* proj; assertProjectOpened(m_projName, proj);QVERIFY(proj); assertSpyCaughtProject(spy, proj); QCOMPARE(proj->projectFile(), m_projFilePath); QCOMPARE(proj->path(), Path(m_scratchDir.absolutePath()+'/')); QVERIFY(m_projCtrl->isProjectNameUsed(m_projName)); } void TestProjectController::closeProject() { m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; IProject* proj = m_projCtrl->findProjectByName(m_projName); Q_ASSERT(proj); QSignalSpy* spy1 = createClosedSpy(); QSignalSpy* spy2 = createClosingSpy(); m_projCtrl->closeProject(proj); QVERIFY(!m_projCtrl->isProjectNameUsed(m_projName)); QCOMPARE(m_projCtrl->projectCount(), 0); assertProjectClosed(proj); assertSpyCaughtProject(spy1, proj); assertSpyCaughtProject(spy2, proj); } void TestProjectController::openCloseOpen() { m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; IProject* proj; assertProjectOpened(m_projName, proj); m_projCtrl->closeProject(proj); QSignalSpy* spy = createOpenedSpy(); m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; QVERIFY(m_projCtrl->isProjectNameUsed(m_projName)); QCOMPARE(m_projCtrl->projectCount(), 1); assertProjectOpened(m_projName, proj); assertSpyCaughtProject(spy, proj); } void TestProjectController::reopen() { m_projCtrl->setDialogProvider(new DialogProviderFake); m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; QSignalSpy* spy = createOpenedSpy(); m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; QCOMPARE(m_projCtrl->projectCount(), 1); QVERIFY(m_projCtrl->isProjectNameUsed(m_projName)); IProject* proj; assertProjectOpened(m_projName, proj); assertSpyCaughtProject(spy, proj); } void TestProjectController::reopenWhileLoading() { // Open the same project again while the first is still // loading. The second open request should be blocked. m_projCtrl->setDialogProvider(new DialogProviderFake); QSignalSpy* spy = createOpenedSpy(); m_projCtrl->openProject(m_projFilePath.toUrl()); //m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; // wait a bit for a second signal, this should timeout QSignalSpy signal(m_projCtrl, SIGNAL(projectOpened(KDevelop::IProject*))); QVERIFY2(!signal.wait(100), "Received 2 projectOpened signals."); QCOMPARE(m_projCtrl->projectCount(), 1); IProject* proj; assertProjectOpened(m_projName, proj); assertSpyCaughtProject(spy, proj); } void TestProjectController::openMultiple() { QString secondProj(QStringLiteral("bar")); Path secondCfgUrl = writeProjectConfig(secondProj); QSignalSpy* spy = createOpenedSpy(); m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; m_projCtrl->openProject(secondCfgUrl.toUrl()); WAIT_FOR_OPEN_SIGNAL; QCOMPARE(m_projCtrl->projectCount(), 2); IProject *proj1, *proj2; assertProjectOpened(m_projName, proj1); assertProjectOpened(secondProj, proj2); QVERIFY(m_projCtrl->isProjectNameUsed(m_projName)); QVERIFY(m_projCtrl->isProjectNameUsed(QStringLiteral("bar"))); QCOMPARE(spy->size(), 2); IProject* emittedProj1 = (*spy)[0][0].value(); IProject* emittedProj2 = (*spy)[1][0].value(); QCOMPARE(emittedProj1, proj1); QCOMPARE(emittedProj2, proj2); m_tmpConfigs << secondCfgUrl; } /*! Verify that the projectmodel contains a single project. Put this project's * ProjectFolderItem in the output parameter @p RootItem */ #define ASSERT_SINGLE_PROJECT_IN_MODEL(rootItem) \ {\ QCOMPARE(m_projCtrl->projectModel()->rowCount(), 1); \ QModelIndex projIndex = m_projCtrl->projectModel()->index(0,0); \ QVERIFY(projIndex.isValid()); \ ProjectBaseItem* i = m_projCtrl->projectModel()->itemFromIndex( projIndex ); \ QVERIFY(i); \ QVERIFY(i->folder()); \ rootItem = i->folder();\ } void(0) /*! Verify that the projectitem @p item has a single child item * named @p name with url @p url. @p subFolder is an output parameter * that contains the sub-folder projectitem. */ #define ASSERT_SINGLE_SUBFOLDER_IN(item, name, path__, subFolder) \ {\ QCOMPARE(item->rowCount(), 1);\ QCOMPARE(item->folderList().size(), 1);\ ProjectFolderItem* fo = item->folderList().at(0);\ QVERIFY(fo);\ QCOMPARE(fo->path(), path__);\ QCOMPARE(fo->folderName(), QStringLiteral(name));\ subFolder = fo;\ } void(0) #define ASSERT_SINGLE_FILE_IN(rootFolder, name, path__, fileItem)\ {\ QCOMPARE(rootFolder->rowCount(), 1);\ QCOMPARE(rootFolder->fileList().size(), 1);\ fileItem = rootFolder->fileList().at(0);\ QVERIFY(fileItem);\ QCOMPARE(fileItem->path(), path__);\ QCOMPARE(fileItem->fileName(), QStringLiteral(name));\ } void(0) // command void TestProjectController::emptyProject() { // verify that the project model contains a single top-level folder after loading // an empty project assertEmptyProjectModel(); m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; Project* proj; assertProjectOpened(m_projName, (KDevelop::IProject*&)proj); FakeFileManager* fileMng = createFileManager(); Q_ASSERT(fileMng); proj->setManagerPlugin(fileMng); proj->reloadModel(); QTest::qWait(100); ProjectFolderItem* rootFolder; ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder); // check that the project is empty QCOMPARE(rootFolder->rowCount(), 0); QCOMPARE(rootFolder->project()->name(), m_projName); QCOMPARE(rootFolder->path(), m_projFolder); } // command void TestProjectController::singleFile() { // verify that the project model contains a single file in the // top folder. First setup a FakeFileManager with this file m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; Project* proj; assertProjectOpened(m_projName, (KDevelop::IProject*&)proj); FakeFileManager* fileMng = createFileManager(); proj->setManagerPlugin(fileMng); Path filePath = Path(m_projFolder, QStringLiteral("foobar")); fileMng->addFileToFolder(m_projFolder, filePath); proj->reloadModel(); QTest::qWait(100); // NO signals for reload ... ProjectFolderItem* rootFolder; ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder); ProjectFileItem* fi; ASSERT_SINGLE_FILE_IN(rootFolder, "foobar", filePath, fi); QCOMPARE(fi->rowCount(), 0); ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder); ASSERT_SINGLE_FILE_IN(rootFolder, "foobar", filePath, fi); } // command void TestProjectController::singleDirectory() { // verify that the project model contains a single folder in the // top folder. First setup a FakeFileManager with this folder m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; Project* proj; assertProjectOpened(m_projName, (KDevelop::IProject*&)proj); Path folderPath = Path(m_projFolder, QStringLiteral("foobar/")); FakeFileManager* fileMng = createFileManager(); fileMng->addSubFolderTo(m_projFolder, folderPath); proj->setManagerPlugin(fileMng); proj->reloadModel(); QTest::qWait(100); ProjectFolderItem* rootFolder; ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder); // check that the project contains a single subfolder ProjectFolderItem* sub; ASSERT_SINGLE_SUBFOLDER_IN(rootFolder, "foobar", folderPath, sub); QCOMPARE(sub->rowCount(), 0); } // command void TestProjectController::fileInSubdirectory() { // verify that the project model contains a single file in a subfolder // First setup a FakeFileManager with this folder + file m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; Project* proj; assertProjectOpened(m_projName, (KDevelop::IProject*&)proj); Path folderPath = Path(m_projFolder, QStringLiteral("foobar/")); FakeFileManager* fileMng = createFileManager(); fileMng->addSubFolderTo(m_projFolder, folderPath); Path filePath = Path(folderPath, QStringLiteral("zoo")); fileMng->addFileToFolder(folderPath, filePath); proj->setManagerPlugin(fileMng); ProjectFolderItem* rootFolder = nullptr; ProjectFolderItem* sub = nullptr; ProjectFileItem* file = nullptr; proj->reloadModel(); QTest::qWait(100); ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder); ASSERT_SINGLE_SUBFOLDER_IN(rootFolder, "foobar", folderPath, sub); ASSERT_SINGLE_FILE_IN(sub,"zoo",filePath,file); ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder); ASSERT_SINGLE_SUBFOLDER_IN(rootFolder, "foobar", folderPath, sub); ASSERT_SINGLE_FILE_IN(sub,"zoo",filePath,file); } void TestProjectController::prettyFileName_data() { QTest::addColumn("relativeFilePath"); QTest::newRow("basic") << "foobar.txt"; QTest::newRow("subfolder") << "sub/foobar.txt"; } void TestProjectController::prettyFileName() { QFETCH(QString, relativeFilePath); m_projCtrl->openProject(m_projFilePath.toUrl()); WAIT_FOR_OPEN_SIGNAL; Project* proj; assertProjectOpened(m_projName, (KDevelop::IProject*&)proj); FakeFileManager* fileMng = createFileManager(); proj->setManagerPlugin(fileMng); Path filePath = Path(m_projFolder, relativeFilePath); fileMng->addFileToFolder(m_projFolder, filePath); QCOMPARE(m_projCtrl->prettyFileName(filePath.toUrl(), ProjectController::FormattingOptions::FormatPlain), QString(m_projName + ':' + relativeFilePath)); } ////////////////////// Helpers /////////////////////////////////////////////// Path TestProjectController::writeProjectConfig(const QString& name) { Path configPath = Path(m_scratchDir.absolutePath() + '/' + name + ".kdev4"); QFile f(configPath.pathOrUrl()); f.open(QIODevice::WriteOnly); QTextStream str(&f); str << "[Project]\n" << "Name=" << name << "\n"; f.close(); return configPath; } ////////////////// Custom assertions ///////////////////////////////////////// void TestProjectController::assertProjectOpened(const QString& name, IProject*& proj) { QVERIFY(proj = m_projCtrl->findProjectByName(name)); QVERIFY(m_projCtrl->projects().contains(proj)); } void TestProjectController::assertSpyCaughtProject(QSignalSpy* spy, IProject* proj) { QCOMPARE(spy->size(), 1); IProject* emittedProj = (*spy)[0][0].value(); QCOMPARE(proj, emittedProj); } void TestProjectController::assertProjectClosed(IProject* proj) { IProject* p = m_projCtrl->findProjectByName(proj->name()); QVERIFY(p == nullptr); QVERIFY(!m_projCtrl->projects().contains(proj)); } void TestProjectController::assertEmptyProjectModel() { ProjectModel* m = m_projCtrl->projectModel(); Q_ASSERT(m); QCOMPARE(m->rowCount(), 0); } ///////////////////// Creation stuff ///////////////////////////////////////// QSignalSpy* TestProjectController::createOpenedSpy() { return new QSignalSpy(m_projCtrl, SIGNAL(projectOpened(KDevelop::IProject*))); } QSignalSpy* TestProjectController::createClosedSpy() { return new QSignalSpy(m_projCtrl, SIGNAL(projectClosed(KDevelop::IProject*))); } QSignalSpy* TestProjectController::createClosingSpy() { return new QSignalSpy(m_projCtrl, SIGNAL(projectClosing(KDevelop::IProject*))); } FakeFileManager* TestProjectController::createFileManager() { FakeFileManager* fileMng = new FakeFileManager; m_fileManagerGarbage << fileMng; return fileMng; } QTEST_MAIN(TestProjectController) #include "moc_test_projectcontroller.cpp" #include "test_projectcontroller.moc" diff --git a/plugins/debuggercommon/tests/debuggees/debugeethreads.cpp b/plugins/debuggercommon/tests/debuggees/debugeethreads.cpp index 854971b93b..f3a327a731 100644 --- a/plugins/debuggercommon/tests/debuggees/debugeethreads.cpp +++ b/plugins/debuggercommon/tests/debuggees/debugeethreads.cpp @@ -1,44 +1,44 @@ /* Copyright 2009 Niko Sams This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #ifndef _MSC_VER #include #endif class TestThread : public QThread { public: void run() override { sleep(1); std::cout << "Hello, world!" << std::endl; } }; -int main(int /*argc*/, char **/*argv*/) { +int main(int /*argc*/, char** /*argv*/) { TestThread t1; TestThread t2; TestThread t3; t1.start(); t2.start(); t3.start(); QThread::usleep(500000); QThread::usleep(600000); return 0; } diff --git a/plugins/gdb/gdboutputwidget.cpp b/plugins/gdb/gdboutputwidget.cpp index f29fd42e0a..54755f4fb5 100644 --- a/plugins/gdb/gdboutputwidget.cpp +++ b/plugins/gdb/gdboutputwidget.cpp @@ -1,459 +1,459 @@ /* * GDB Debugger Support * * Copyright 2003 John Birch * Copyright 2006 Vladimir Prus * Copyright 2007 Hamish Rodda * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "gdboutputwidget.h" #include "dbgglobal.h" #include "debuggerplugin.h" #include "debuglog.h" #include "debugsession.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevMI::GDB; /***************************************************************************/ GDBOutputWidget::GDBOutputWidget(CppDebuggerPlugin* plugin, QWidget *parent) : QWidget(parent), m_userGDBCmdEditor(nullptr), m_Interrupt(nullptr), m_gdbView(nullptr), m_showInternalCommands(false), m_maxLines(5000) { setWindowIcon(QIcon::fromTheme(QStringLiteral("dialog-scripts"), windowIcon())); setWindowTitle(i18n("GDB Output")); setWhatsThis(i18n("GDB output

" "Shows all gdb commands being executed. " "You can also issue any other gdb command while debugging.

")); m_gdbView = new OutputTextEdit(this); m_gdbView->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); m_gdbView->setReadOnly(true); m_userGDBCmdEditor = new KHistoryComboBox (this); QLabel *label = new QLabel(i18n("&GDB cmd:"), this); label->setBuddy(m_userGDBCmdEditor); m_Interrupt = new QToolButton( this ); m_Interrupt->setIcon ( QIcon::fromTheme( QStringLiteral("media-playback-pause") ) ); m_Interrupt->setToolTip( i18n ( "Pause execution of the app to enter gdb commands" ) ); QVBoxLayout *topLayout = new QVBoxLayout(this); topLayout->addWidget(m_gdbView); topLayout->setStretchFactor(m_gdbView, 1); topLayout->setMargin(0); QBoxLayout *userGDBCmdEntry = new QHBoxLayout(); userGDBCmdEntry->addWidget(label); userGDBCmdEntry->addWidget(m_userGDBCmdEditor); userGDBCmdEntry->setStretchFactor(m_userGDBCmdEditor, 1); userGDBCmdEntry->addWidget(m_Interrupt); topLayout->addLayout(userGDBCmdEntry); setLayout(topLayout); slotStateChanged(s_none, s_dbgNotStarted); connect(m_userGDBCmdEditor, static_cast(&KHistoryComboBox::returnPressed), this, &GDBOutputWidget::slotGDBCmd); connect(m_Interrupt, &QToolButton::clicked, this, &GDBOutputWidget::breakInto); m_updateTimer.setSingleShot(true); connect(&m_updateTimer, &QTimer::timeout, this, &GDBOutputWidget::flushPending); connect(KDevelop::ICore::self()->debugController(), &KDevelop::IDebugController::currentSessionChanged, this, &GDBOutputWidget::currentSessionChanged); connect(plugin, &CppDebuggerPlugin::reset, this, &GDBOutputWidget::clear); connect(plugin, &CppDebuggerPlugin::raiseDebuggerConsoleViews, this, &GDBOutputWidget::requestRaise); currentSessionChanged(KDevelop::ICore::self()->debugController()->currentSession()); // TODO Port to KF5 // connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), // this, SLOT(updateColors())); updateColors(); } void GDBOutputWidget::updateColors() { KColorScheme scheme(QPalette::Active); m_gdbColor = scheme.foreground(KColorScheme::LinkText).color(); m_errorColor = scheme.foreground(KColorScheme::NegativeText).color(); } void GDBOutputWidget::currentSessionChanged(KDevelop::IDebugSession* s) { if (!s) return; DebugSession *session = qobject_cast(s); if (!session) return; connect(this, &GDBOutputWidget::userGDBCmd, session, &DebugSession::addUserCommand); connect(this, &GDBOutputWidget::breakInto, session, &DebugSession::interruptDebugger); connect(session, &DebugSession::debuggerInternalCommandOutput, this, &GDBOutputWidget::slotInternalCommandStdout); connect(session, &DebugSession::debuggerUserCommandOutput, this, &GDBOutputWidget::slotUserCommandStdout); // debugger internal output, treat it as an internal command output connect(session, &DebugSession::debuggerInternalOutput, this, &GDBOutputWidget::slotInternalCommandStdout); connect(session, &DebugSession::debuggerStateChanged, this, &GDBOutputWidget::slotStateChanged); slotStateChanged(s_none, session->debuggerState()); } /***************************************************************************/ GDBOutputWidget::~GDBOutputWidget() { delete m_gdbView; delete m_userGDBCmdEditor; } /***************************************************************************/ void GDBOutputWidget::clear() { if (m_gdbView) m_gdbView->clear(); m_userCommands_.clear(); m_allCommands.clear(); } /***************************************************************************/ void GDBOutputWidget::slotInternalCommandStdout(const QString& line) { newStdoutLine(line, true); } void GDBOutputWidget::slotUserCommandStdout(const QString& line) { qCDebug(DEBUGGERGDB) << "User command stdout: " << line; newStdoutLine(line, false); } namespace { QString colorify(QString text, const QColor& color) { // Make sure the newline is at the end of the newly-added // string. This is so that we can always correctly remove // newline inside 'flushPending'. if (!text.endsWith('\n')) text.append('\n'); if (text.endsWith('\n')) { text.remove(text.length()-1, 1); } text = "" + text + "
"; return text; } } void GDBOutputWidget::newStdoutLine(const QString& line, bool internal) { QString s = line.toHtmlEscaped(); if (s.startsWith(QLatin1String("(gdb)"))) { s = colorify(s, m_gdbColor); } else s.replace('\n', QLatin1String("
")); m_allCommands.append(s); m_allCommandsRaw.append(line); trimList(m_allCommands, m_maxLines); trimList(m_allCommandsRaw, m_maxLines); if (!internal) { m_userCommands_.append(s); m_userCommandsRaw.append(line); trimList(m_userCommands_, m_maxLines); trimList(m_userCommandsRaw, m_maxLines); } if (!internal || m_showInternalCommands) showLine(s); } void GDBOutputWidget::showLine(const QString& line) { m_pendingOutput += line; // To improve performance, we update the view after some delay. if (!m_updateTimer.isActive()) { m_updateTimer.start(100); } } void GDBOutputWidget::trimList(QStringList& l, int max_size) { int length = l.count(); if (length > max_size) { for(int to_delete = length - max_size; to_delete; --to_delete) { l.erase(l.begin()); } } } void GDBOutputWidget::setShowInternalCommands(bool show) { if (show != m_showInternalCommands) { m_showInternalCommands = show; // Set of strings to show changes, text edit still has old // set. Refresh. m_gdbView->clear(); QStringList& newList = m_showInternalCommands ? m_allCommands : m_userCommands_; QStringList::iterator i = newList.begin(), e = newList.end(); for(; i != e; ++i) { // Note that color formatting is already applied to '*i'. showLine(*i); } } } /***************************************************************************/ void GDBOutputWidget::slotReceivedStderr(const char* line) { QString colored = colorify(QString::fromLatin1(line).toHtmlEscaped(), m_errorColor); // Errors are shown inside user commands too. m_allCommands.append(colored); trimList(m_allCommands, m_maxLines); m_userCommands_.append(colored); trimList(m_userCommands_, m_maxLines); m_allCommandsRaw.append(line); trimList(m_allCommandsRaw, m_maxLines); m_userCommandsRaw.append(line); trimList(m_userCommandsRaw, m_maxLines); showLine(colored); } /***************************************************************************/ void GDBOutputWidget::slotGDBCmd() { QString GDBCmd(m_userGDBCmdEditor->currentText()); if (!GDBCmd.isEmpty()) { m_userGDBCmdEditor->addToHistory(GDBCmd); m_userGDBCmdEditor->clearEditText(); emit userGDBCmd(GDBCmd); } } void GDBOutputWidget::flushPending() { m_gdbView->setUpdatesEnabled(false); // QTextEdit adds newline after paragraph automatically. // So, remove trailing newline to avoid double newlines. if (m_pendingOutput.endsWith('\n')) m_pendingOutput.remove(m_pendingOutput.length()-1, 1); Q_ASSERT(!m_pendingOutput.endsWith('\n')); QTextDocument *document = m_gdbView->document(); QTextCursor cursor(document); cursor.movePosition(QTextCursor::End); cursor.insertHtml(m_pendingOutput); m_pendingOutput.clear(); m_gdbView->verticalScrollBar()->setValue(m_gdbView->verticalScrollBar()->maximum()); m_gdbView->setUpdatesEnabled(true); m_gdbView->update(); if (m_cmdEditorHadFocus) { m_userGDBCmdEditor->setFocus(); } } /***************************************************************************/ void GDBOutputWidget::slotStateChanged(KDevMI::DBGStateFlags oldStatus, KDevMI::DBGStateFlags newStatus) { Q_UNUSED(oldStatus) if (newStatus & s_dbgNotStarted) { m_Interrupt->setEnabled(false); m_userGDBCmdEditor->setEnabled(false); return; } else { m_Interrupt->setEnabled(true); } if (newStatus & s_dbgBusy) { if (m_userGDBCmdEditor->isEnabled()) { m_cmdEditorHadFocus = m_userGDBCmdEditor->hasFocus(); } m_userGDBCmdEditor->setEnabled(false); } else { m_userGDBCmdEditor->setEnabled(true); } } /***************************************************************************/ -void GDBOutputWidget::focusInEvent(QFocusEvent */*e*/) +void GDBOutputWidget::focusInEvent(QFocusEvent* /*e*/) { m_gdbView->verticalScrollBar()->setValue(m_gdbView->verticalScrollBar()->maximum()); m_userGDBCmdEditor->setFocus(); } void GDBOutputWidget::savePartialProjectSession() { KConfigGroup config(KSharedConfig::openConfig(), "GDB Debugger"); config.writeEntry("showInternalCommands", m_showInternalCommands); } void GDBOutputWidget::restorePartialProjectSession() { KConfigGroup config(KSharedConfig::openConfig(), "GDB Debugger"); m_showInternalCommands = config.readEntry("showInternalCommands", false); } void GDBOutputWidget::contextMenuEvent(QContextMenuEvent * e) { QScopedPointer popup(new QMenu(this)); QAction* action = popup->addAction(i18n("Show Internal Commands"), this, SLOT(toggleShowInternalCommands())); action->setCheckable(true); action->setChecked(m_showInternalCommands); action->setWhatsThis(i18n( "Controls if commands issued internally by KDevelop " "will be shown or not.
" "This option will affect only future commands, it will not " "add or remove already issued commands from the view.")); popup->addAction(i18n("Copy All"), this, SLOT(copyAll())); popup->exec(e->globalPos()); } void GDBOutputWidget::copyAll() { /* See comments for allCommandRaw_ for explanations of this complex logic, as opposed to calling text(). */ const QStringList& raw = m_showInternalCommands ? m_allCommandsRaw : m_userCommandsRaw; QString text; for (int i = 0; i < raw.size(); ++i) text += raw.at(i); // Make sure the text is pastable both with Ctrl-C and with // middle click. QApplication::clipboard()->setText(text, QClipboard::Clipboard); QApplication::clipboard()->setText(text, QClipboard::Selection); } void GDBOutputWidget::toggleShowInternalCommands() { setShowInternalCommands(!m_showInternalCommands); } OutputTextEdit::OutputTextEdit(GDBOutputWidget * parent) : QPlainTextEdit(parent) { } void OutputTextEdit::contextMenuEvent(QContextMenuEvent * event) { QScopedPointer popup(createStandardContextMenu()); QAction* action = popup->addAction(i18n("Show Internal Commands"), parent(), SLOT(toggleShowInternalCommands())); action->setCheckable(true); action->setChecked(static_cast(parent())->showInternalCommands()); action->setWhatsThis(i18n( "Controls if commands issued internally by KDevelop " "will be shown or not.
" "This option will affect only future commands, it will not " "add or remove already issued commands from the view.")); popup->exec(event->globalPos()); } bool GDBOutputWidget::showInternalCommands() const { return m_showInternalCommands; }