(*r);
if (s.subkind == MI::StreamRecord::Target) {
emit applicationOutput(s.message);
} else if (s.subkind == MI::StreamRecord::Console) {
if (m_currentCmd && m_currentCmd->isUserCommand())
emit userCommandOutput(s.message);
else
emit internalCommandOutput(s.message);
if (m_currentCmd)
m_currentCmd->newOutput(s.message);
} else {
emit debuggerInternalOutput(s.message);
}
emit streamRecord(s);
break;
}
case MI::Record::Prompt:
break;
}
#ifndef DEBUG_NO_TRY
}
catch(const std::exception& e)
{
KMessageBox::detailedSorry(
qApp->activeWindow(),
i18nc("Internal debugger error",
"The debugger component encountered internal error while "
"processing reply from gdb. Please submit a bug report. "
"The debug session will now end to prevent potential crash"),
i18n("The exception is: %1\n"
"The MI response is: %2", e.what(),
QString::fromLatin1(line)),
i18n("Internal debugger error"));
emit exited(true, e.what());
}
#endif
}
void MIDebugger::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
qCDebug(DEBUGGERCOMMON) << "Debugger FINISHED\n";
bool abnormal = exitCode != 0 || exitStatus != QProcess::NormalExit;
emit userCommandOutput(QStringLiteral("Process exited\n"));
emit exited(abnormal, i18n("Process exited"));
}
void MIDebugger::processErrored(QProcess::ProcessError error)
{
qCDebug(DEBUGGERCOMMON) << "Debugger ERRORED" << error;
if(error == QProcess::FailedToStart)
{
KMessageBox::information(
qApp->activeWindow(),
i18n("Could not start debugger."
"
Could not run '%1'. "
"Make sure that the path name is specified correctly.",
m_debuggerExecutable),
i18n("Could not start debugger"));
emit userCommandOutput(QStringLiteral("Process failed to start\n"));
emit exited(true, i18n("Process failed to start"));
} else if (error == QProcess::Crashed) {
KMessageBox::error(
qApp->activeWindow(),
i18n("Debugger crashed."
"
The debugger process '%1' crashed.
"
"Because of that the debug session has to be ended.
"
"Try to reproduce the crash without KDevelop and report a bug.
",
m_debuggerExecutable),
i18n("Debugger crashed"));
emit userCommandOutput(QStringLiteral("Process crashed\n"));
emit exited(true, i18n("Process crashed"));
}
}
diff --git a/plugins/debuggercommon/widgets/disassemblewidget.cpp b/plugins/debuggercommon/widgets/disassemblewidget.cpp
index 6552f8fa89..a6fc1919a0 100644
--- a/plugins/debuggercommon/widgets/disassemblewidget.cpp
+++ b/plugins/debuggercommon/widgets/disassemblewidget.cpp
@@ -1,539 +1,538 @@
/*
* GDB Debugger Support
*
* Copyright 2000 John Birch
* Copyright 2006 Vladimir Prus
* Copyright 2007 Hamish Rodda
* Copyright 2013 Vlas Puhov
*
* 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 "disassemblewidget.h"
#include "midebuggerplugin.h"
#include "debuglog.h"
#include "midebugsession.h"
#include "mi/micommand.h"
#include "registers/registersmanager.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace KDevMI;
using namespace KDevMI::MI;
SelectAddressDialog::SelectAddressDialog(QWidget* parent)
: QDialog(parent)
{
m_ui.setupUi(this);
setWindowTitle(i18n("Address Selector"));
connect(m_ui.comboBox, &KHistoryComboBox::editTextChanged,
this, &SelectAddressDialog::validateInput);
connect(m_ui.comboBox, static_cast(&KHistoryComboBox::returnPressed),
this, &SelectAddressDialog::itemSelected);
}
QString SelectAddressDialog::address() const
{
return hasValidAddress() ? m_ui.comboBox->currentText() : QString();
}
bool SelectAddressDialog::hasValidAddress() const
{
bool ok;
m_ui.comboBox->currentText().toLongLong(&ok, 16);
return ok;
}
void SelectAddressDialog::updateOkState()
{
m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(hasValidAddress());
}
void SelectAddressDialog::validateInput()
{
updateOkState();
}
void SelectAddressDialog::itemSelected()
{
QString text = m_ui.comboBox->currentText();
if( hasValidAddress() && m_ui.comboBox->findText(text) < 0 )
m_ui.comboBox->addItem(text);
}
DisassembleWindow::DisassembleWindow(QWidget *parent, DisassembleWidget* widget)
: QTreeWidget(parent)
{
/*context menu commands */{
m_selectAddrAction = new QAction(i18n("Change &address"), this);
m_selectAddrAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);
connect(m_selectAddrAction, &QAction::triggered, widget, &DisassembleWidget::slotChangeAddress);
m_jumpToLocation = new QAction(QIcon::fromTheme(QStringLiteral("debug-execute-to-cursor")), i18n("&Jump to Cursor"), this);
m_jumpToLocation->setWhatsThis(i18n("Sets the execution pointer to the current cursor position."));
connect(m_jumpToLocation,&QAction::triggered, widget, &DisassembleWidget::jumpToCursor);
m_runUntilCursor = new QAction(QIcon::fromTheme(QStringLiteral("debug-run-cursor")), i18n("&Run to Cursor"), this);
m_runUntilCursor->setWhatsThis(i18n("Continues execution until the cursor position is reached."));
connect(m_runUntilCursor,&QAction::triggered, widget, &DisassembleWidget::runToCursor);
m_disassemblyFlavorAtt = new QAction(i18n("&AT&&T"), this);
m_disassemblyFlavorAtt->setToolTip(i18n("GDB will use the AT&T disassembly flavor (e.g. mov 0xc(%ebp),%eax)."));
m_disassemblyFlavorAtt->setData(DisassemblyFlavorATT);
m_disassemblyFlavorAtt->setCheckable(true);
m_disassemblyFlavorIntel = new QAction(i18n("&Intel"), this);
m_disassemblyFlavorIntel->setToolTip(i18n("GDB will use the Intel disassembly flavor (e.g. mov eax, DWORD PTR [ebp+0xc])."));
m_disassemblyFlavorIntel->setData(DisassemblyFlavorIntel);
m_disassemblyFlavorIntel->setCheckable(true);
m_disassemblyFlavorActionGroup = new QActionGroup(this);
m_disassemblyFlavorActionGroup->setExclusive(true);
m_disassemblyFlavorActionGroup->addAction(m_disassemblyFlavorAtt);
m_disassemblyFlavorActionGroup->addAction(m_disassemblyFlavorIntel);
connect(m_disassemblyFlavorActionGroup, &QActionGroup::triggered, widget, &DisassembleWidget::setDisassemblyFlavor);
}
}
void DisassembleWindow::setDisassemblyFlavor(DisassemblyFlavor flavor)
{
switch(flavor)
{
- default:
case DisassemblyFlavorUnknown:
m_disassemblyFlavorAtt->setChecked(false);
m_disassemblyFlavorIntel->setChecked(false);
break;
case DisassemblyFlavorATT:
m_disassemblyFlavorAtt->setChecked(true);
m_disassemblyFlavorIntel->setChecked(false);
break;
case DisassemblyFlavorIntel:
m_disassemblyFlavorAtt->setChecked(false);
m_disassemblyFlavorIntel->setChecked(true);
break;
}
}
void DisassembleWindow::contextMenuEvent(QContextMenuEvent *e)
{
QMenu popup(this);
popup.addAction(m_selectAddrAction);
popup.addAction(m_jumpToLocation);
popup.addAction(m_runUntilCursor);
QMenu* disassemblyFlavorMenu = popup.addMenu(i18n("Disassembly flavor"));
disassemblyFlavorMenu->addAction(m_disassemblyFlavorAtt);
disassemblyFlavorMenu->addAction(m_disassemblyFlavorIntel);
popup.exec(e->globalPos());
}
/***************************************************************************/
/***************************************************************************/
/***************************************************************************/
DisassembleWidget::DisassembleWidget(MIDebuggerPlugin* plugin, QWidget *parent)
: QWidget(parent),
active_(false),
lower_(0),
upper_(0),
address_(0),
m_splitter(new KDevelop::AutoOrientedSplitter(this))
{
QVBoxLayout* topLayout = new QVBoxLayout(this);
topLayout->setMargin(0);
QHBoxLayout* controlsLayout = new QHBoxLayout;
topLayout->addLayout(controlsLayout);
{ // initialize disasm/registers views
topLayout->addWidget(m_splitter);
//topLayout->setMargin(0);
m_disassembleWindow = new DisassembleWindow(m_splitter, this);
m_disassembleWindow->setWhatsThis(i18n("Machine code display"
"A machine code view into your running "
"executable with the current instruction "
"highlighted. You can step instruction by "
"instruction using the debuggers toolbar "
"buttons of \"step over\" instruction and "
"\"step into\" instruction."));
m_disassembleWindow->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
m_disassembleWindow->setSelectionMode(QTreeWidget::SingleSelection);
m_disassembleWindow->setColumnCount(ColumnCount);
m_disassembleWindow->setUniformRowHeights(true);
m_disassembleWindow->setRootIsDecorated(false);
m_disassembleWindow->setHeaderLabels(QStringList() << QLatin1String("") << i18n("Address") << i18n("Function") << i18n("Instruction"));
m_splitter->setStretchFactor(0, 1);
m_splitter->setContentsMargins(0, 0, 0, 0);
m_registersManager = new RegistersManager(m_splitter);
m_config = KSharedConfig::openConfig()->group("Disassemble/Registers View");
QByteArray state = m_config.readEntry("splitterState", QByteArray());
if (!state.isEmpty()) {
m_splitter->restoreState(state);
}
}
setLayout(topLayout);
setWindowIcon( QIcon::fromTheme(QStringLiteral("system-run"), windowIcon()) );
setWindowTitle(i18n("Disassemble/Registers View"));
KDevelop::IDebugController* pDC=KDevelop::ICore::self()->debugController();
Q_ASSERT(pDC);
connect(pDC,
&KDevelop::IDebugController::currentSessionChanged,
this, &DisassembleWidget::currentSessionChanged);
connect(plugin, &MIDebuggerPlugin::reset, this, &DisassembleWidget::slotDeactivate);
m_dlg = new SelectAddressDialog(this);
// show the data if debug session is active
KDevelop::IDebugSession* pS = pDC->currentSession();
currentSessionChanged(pS);
if(pS && !pS->currentAddr().isEmpty())
slotShowStepInSource(pS->currentUrl(), pS->currentLine(), pS->currentAddr());
}
void DisassembleWidget::jumpToCursor() {
MIDebugSession *s = qobject_cast(KDevelop::ICore::
self()->debugController()->currentSession());
if (s && s->isRunning()) {
QString address = m_disassembleWindow->selectedItems().at(0)->text(Address);
s->jumpToMemoryAddress(address);
}
}
void DisassembleWidget::runToCursor(){
MIDebugSession *s = qobject_cast(KDevelop::ICore::
self()->debugController()->currentSession());
if (s && s->isRunning()) {
QString address = m_disassembleWindow->selectedItems().at(0)->text(Address);
s->runUntil(address);
}
}
void DisassembleWidget::currentSessionChanged(KDevelop::IDebugSession* s)
{
MIDebugSession *session = qobject_cast(s);
enableControls( session != nullptr ); // disable if session closed
m_registersManager->setSession(session);
if (session) {
connect(session, &MIDebugSession::showStepInSource,
this, &DisassembleWidget::slotShowStepInSource);
connect(session,&MIDebugSession::showStepInDisassemble,this, &DisassembleWidget::update);
}
}
/***************************************************************************/
DisassembleWidget::~DisassembleWidget()
{
m_config.writeEntry("splitterState", m_splitter->saveState());
}
/***************************************************************************/
bool DisassembleWidget::displayCurrent()
{
if(address_ < lower_ || address_ > upper_) return false;
bool bFound=false;
for (int line=0; line < m_disassembleWindow->topLevelItemCount(); line++)
{
QTreeWidgetItem* item = m_disassembleWindow->topLevelItem(line);
unsigned long address = item->text(Address).toULong(&ok,16);
if (address == address_)
{
// put cursor at start of line and highlight the line
m_disassembleWindow->setCurrentItem(item);
static const QIcon icon = QIcon::fromTheme(QStringLiteral("go-next"));
item->setIcon(Icon, icon);
bFound = true; // need to process all items to clear icons
}
else if(!item->icon(Icon).isNull()) item->setIcon(Icon, QIcon());
}
return bFound;
}
/***************************************************************************/
void DisassembleWidget::slotActivate(bool activate)
{
qCDebug(DEBUGGERCOMMON) << "Disassemble widget active: " << activate;
if (active_ != activate)
{
active_ = activate;
if (active_)
{
updateDisassemblyFlavor();
m_registersManager->updateRegisters();
if (!displayCurrent())
disassembleMemoryRegion();
}
}
}
/***************************************************************************/
void DisassembleWidget::slotShowStepInSource(const QUrl&, int,
const QString& currentAddress)
{
update(currentAddress);
}
void DisassembleWidget::updateExecutionAddressHandler(const ResultRecord& r)
{
const Value& content = r[QStringLiteral("asm_insns")];
const Value& pc = content[0];
if( pc.hasField(QStringLiteral("address")) ){
QString addr = pc[QStringLiteral("address")].literal();
address_ = addr.toULong(&ok,16);
disassembleMemoryRegion(addr);
}
}
/***************************************************************************/
void DisassembleWidget::disassembleMemoryRegion(const QString& from, const QString& to)
{
MIDebugSession *s = qobject_cast(KDevelop::ICore::
self()->debugController()->currentSession());
if(!s || !s->isRunning()) return;
//only get $pc
if (from.isEmpty()){
s->addCommand(DataDisassemble, QStringLiteral("-s \"$pc\" -e \"$pc+1\" -- 0"),
this, &DisassembleWidget::updateExecutionAddressHandler);
}else{
QString cmd = (to.isEmpty())?
QStringLiteral("-s %1 -e \"%1 + 256\" -- 0").arg(from ):
QStringLiteral("-s %1 -e %2+1 -- 0").arg(from, to); // if both addr set
s->addCommand(DataDisassemble, cmd,
this, &DisassembleWidget::disassembleMemoryHandler);
}
}
/***************************************************************************/
void DisassembleWidget::disassembleMemoryHandler(const ResultRecord& r)
{
const Value& content = r[QStringLiteral("asm_insns")];
QString currentFunction;
m_disassembleWindow->clear();
for(int i = 0; i < content.size(); ++i)
{
const Value& line = content[i];
QString addr, fct, offs, inst;
if( line.hasField(QStringLiteral("address")) ) addr = line[QStringLiteral("address")].literal();
if( line.hasField(QStringLiteral("func-name")) ) fct = line[QStringLiteral("func-name")].literal();
if( line.hasField(QStringLiteral("offset")) ) offs = line[QStringLiteral("offset")].literal();
if( line.hasField(QStringLiteral("inst")) ) inst = line[QStringLiteral("inst")].literal();
//We use offset at the same column where function is.
if(currentFunction == fct){
if(!fct.isEmpty()){
fct = QStringLiteral("+") + offs;
}
}else { currentFunction = fct; }
m_disassembleWindow->addTopLevelItem(new QTreeWidgetItem(m_disassembleWindow,
QStringList() << QString() << addr << fct << inst));
if (i == 0) {
lower_ = addr.toULong(&ok,16);
} else if (i == content.size()-1) {
upper_ = addr.toULong(&ok,16);
}
}
displayCurrent();
m_disassembleWindow->resizeColumnToContents(Icon); // make Icon always visible
m_disassembleWindow->resizeColumnToContents(Address); // make entire address always visible
}
void DisassembleWidget::showEvent(QShowEvent*)
{
slotActivate(true);
//it doesn't work for large names of functions
// for (int i = 0; i < m_disassembleWindow->model()->columnCount(); ++i)
// m_disassembleWindow->resizeColumnToContents(i);
}
void DisassembleWidget::hideEvent(QHideEvent*)
{
slotActivate(false);
}
void DisassembleWidget::slotDeactivate()
{
slotActivate(false);
}
void DisassembleWidget::enableControls(bool enabled)
{
m_disassembleWindow->setEnabled(enabled);
}
void DisassembleWidget::slotChangeAddress()
{
if(!m_dlg) return;
m_dlg->updateOkState();
if (!m_disassembleWindow->selectedItems().isEmpty()) {
m_dlg->setAddress(m_disassembleWindow->selectedItems().first()->text(Address));
}
if (m_dlg->exec() == QDialog::Rejected)
return;
unsigned long addr = m_dlg->address().toULong(&ok,16);
if (addr < lower_ || addr > upper_ || !displayCurrent())
disassembleMemoryRegion(m_dlg->address());
}
void SelectAddressDialog::setAddress ( const QString& address )
{
m_ui.comboBox->setCurrentItem ( address, true );
}
void DisassembleWidget::update(const QString &address)
{
if (!active_) {
return;
}
address_ = address.toULong(&ok, 16);
if (!displayCurrent()) {
disassembleMemoryRegion();
}
m_registersManager->updateRegisters();
}
void DisassembleWidget::setDisassemblyFlavor(QAction* action)
{
MIDebugSession* s = qobject_cast(KDevelop::ICore::
self()->debugController()->currentSession());
if(!s || !s->isRunning()) {
return;
}
DisassemblyFlavor disassemblyFlavor = static_cast(action->data().toInt());
QString cmd;
switch(disassemblyFlavor)
{
default:
// unknown flavor, do not build a GDB command
break;
case DisassemblyFlavorATT:
cmd = QStringLiteral("disassembly-flavor att");
break;
case DisassemblyFlavorIntel:
cmd = QStringLiteral("disassembly-flavor intel");
break;
}
qCDebug(DEBUGGERCOMMON) << "Disassemble widget set " << cmd;
if (!cmd.isEmpty()) {
s->addCommand(GdbSet, cmd, this, &DisassembleWidget::setDisassemblyFlavorHandler);
}
}
void DisassembleWidget::setDisassemblyFlavorHandler(const ResultRecord& r)
{
if (r.reason == QLatin1String("done") && active_) {
disassembleMemoryRegion();
}
}
void DisassembleWidget::updateDisassemblyFlavor()
{
MIDebugSession* s = qobject_cast(KDevelop::ICore::
self()->debugController()->currentSession());
if(!s || !s->isRunning()) {
return;
}
s->addCommand(GdbShow, QStringLiteral("disassembly-flavor"), this, &DisassembleWidget::showDisassemblyFlavorHandler);
}
void DisassembleWidget::showDisassemblyFlavorHandler(const ResultRecord& r)
{
const Value& value = r[QStringLiteral("value")];
qCDebug(DEBUGGERCOMMON) << "Disassemble widget disassembly flavor" << value.literal();
DisassemblyFlavor disassemblyFlavor = DisassemblyFlavorUnknown;
if (value.literal() == QLatin1String("att")) {
disassemblyFlavor = DisassemblyFlavorATT;
} else if (value.literal() == QLatin1String("intel")) {
disassemblyFlavor = DisassemblyFlavorIntel;
} else if (value.literal() == QLatin1String("default")) {
disassemblyFlavor = DisassemblyFlavorATT;
}
m_disassembleWindow->setDisassemblyFlavor(disassemblyFlavor);
}
diff --git a/plugins/documentview/kdevdocumentmodel.cpp b/plugins/documentview/kdevdocumentmodel.cpp
index 06bfc75182..53074ac13d 100644
--- a/plugins/documentview/kdevdocumentmodel.cpp
+++ b/plugins/documentview/kdevdocumentmodel.cpp
@@ -1,164 +1,163 @@
/* This file is part of KDevelop
Copyright 2005 Adam Treat
Copyright 2013 Sebastian Kügler
This library 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 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 "kdevdocumentmodel.h"
#include
using namespace KDevelop;
KDevDocumentItem::KDevDocumentItem( const QString &name )
: QStandardItem(name)
, m_documentState(IDocument::Clean)
{
setIcon( icon() );
}
KDevDocumentItem::~KDevDocumentItem()
{}
QIcon KDevDocumentItem::icon() const
{
switch(m_documentState)
{
case IDocument::Clean:
return QIcon::fromTheme(m_fileIcon);
case IDocument::Modified:
return QIcon::fromTheme(QStringLiteral("document-save"));
case IDocument::Dirty:
return QIcon::fromTheme(QStringLiteral("document-revert"));
case IDocument::DirtyAndModified:
return QIcon::fromTheme(QStringLiteral("edit-delete"));
- default:
- return QIcon();
}
+ Q_UNREACHABLE();
}
IDocument::DocumentState KDevDocumentItem::documentState() const
{
return m_documentState;
}
void KDevDocumentItem::setDocumentState(IDocument::DocumentState state)
{
m_documentState = state;
setIcon(icon());
}
const QUrl KDevDocumentItem::url() const
{
return m_url;
}
void KDevDocumentItem::setUrl(const QUrl& url)
{
m_url = url;
}
QVariant KDevDocumentItem::data(int role) const
{
if (role == UrlRole) {
return m_url;
}
return QStandardItem::data(role);
}
KDevCategoryItem::KDevCategoryItem( const QString &name )
: KDevDocumentItem( name )
{
setFlags(Qt::ItemIsEnabled);
setToolTip( name );
setIcon( QIcon::fromTheme( QStringLiteral( "folder") ) );
}
KDevCategoryItem::~KDevCategoryItem()
{}
QList KDevCategoryItem::fileList() const
{
QList lst;
for ( int i = 0; i < rowCount(); ++i )
{
if (KDevFileItem* item = static_cast(child(i))->fileItem())
lst.append( item );
}
return lst;
}
KDevFileItem* KDevCategoryItem::file( const QUrl &url ) const
{
foreach( KDevFileItem * item, fileList() )
{
if ( item->url() == url )
return item;
}
return nullptr;
}
KDevFileItem::KDevFileItem( const QUrl &url )
: KDevDocumentItem( url.fileName() )
{
setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
setUrl( url );
if (!url.isEmpty()) {
m_fileIcon = KFileItem(url, QString(), 0).iconName();
}
setIcon( QIcon::fromTheme( m_fileIcon ) );
}
KDevFileItem::~KDevFileItem()
{}
KDevDocumentModel::KDevDocumentModel( QObject *parent )
: QStandardItemModel( parent )
{
setRowCount(0);
setColumnCount(1);
}
KDevDocumentModel::~KDevDocumentModel()
{}
QList KDevDocumentModel::categoryList() const
{
QList lst;
for ( int i = 0; i < rowCount() ; ++i )
{
if (KDevCategoryItem* categoryitem = static_cast(item(i))->categoryItem()) {
lst.append( categoryitem );
}
}
return lst;
}
KDevCategoryItem* KDevDocumentModel::category( const QString& category ) const
{
foreach( KDevCategoryItem * item, categoryList() )
{
if ( item->toolTip() == category )
return item;
}
return nullptr;
}
diff --git a/plugins/filetemplates/overridespage.cpp b/plugins/filetemplates/overridespage.cpp
index 53f71008a1..d6dc828c00 100644
--- a/plugins/filetemplates/overridespage.cpp
+++ b/plugins/filetemplates/overridespage.cpp
@@ -1,274 +1,271 @@
/*
Copyright 2008 Hamish Rodda
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 "overridespage.h"
#include "ui_overridevirtuals.h"
#include "debug.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace KDevelop;
enum Column {
ClassOrFunctionColumn, ///< Column represents either a base class item or a function item
AccessColumn, ///< Column represents the access policy of a function
PropertiesColumn ///< Column represents the properties of a function (e.g. if it's a ctor, dtor, signal, slot, ...)
};
static QString accessPolicyToString(Declaration::AccessPolicy accessPolicy)
{
switch (accessPolicy) {
case Declaration::DefaultAccess:
case Declaration::Public:
return i18n("Public");
case Declaration::Protected:
return i18n("Protected");
case Declaration::Private:
return i18n("Private");
- default:
- qCritical("Unexpected value for Declaration::AccessPolicy: %d", accessPolicy);
- Q_ASSERT(false);
- return QString();
}
+ Q_UNREACHABLE();
}
static QString functionPropertiesToString(ClassFunctionDeclaration* decl)
{
Q_ASSERT(decl);
QStringList properties;
if (decl->isConstructor()) {
properties << i18n("Constructor");
} else if (decl->isDestructor()) {
properties << i18n("Destructor");
} else if (decl->isSignal()) {
properties << i18n("Signal");
} else if (decl->isSlot()) {
properties << i18n("Slot");
} else if (decl->isAbstract()) {
properties << i18n("Abstract function");
}
return properties.join(QStringLiteral(", "));
}
struct KDevelop::OverridesPagePrivate
{
OverridesPagePrivate()
: overrides(nullptr)
{
}
Ui::OverridesDialog* overrides;
QMultiHash overriddenFunctions;
QMap declarationMap;
QList chosenOverrides;
};
OverridesPage::OverridesPage(QWidget* parent)
: QWidget(parent)
, d(new OverridesPagePrivate)
{
d->overrides = new Ui::OverridesDialog;
d->overrides->setupUi(this);
connect(d->overrides->selectAllPushButton, &QPushButton::pressed, this, &OverridesPage::selectAll);
connect(d->overrides->deselectAllPushButton, &QPushButton::pressed, this, &OverridesPage::deselectAll);
}
OverridesPage::~OverridesPage()
{
delete d->overrides;
delete d;
}
QList< DeclarationPointer > OverridesPage::selectedOverrides() const
{
QList declarations;
for (int i = 0; i < d->overrides->overridesTree->topLevelItemCount(); ++i)
{
QTreeWidgetItem* item = d->overrides->overridesTree->topLevelItem(i);
for (int j = 0; j < item->childCount(); ++j)
{
QTreeWidgetItem* child = item->child(j);
if (child->checkState(ClassOrFunctionColumn) == Qt::Checked)
{
qCDebug(PLUGIN_FILETEMPLATES) << "Adding declaration" << d->declarationMap[child]->toString();
declarations << d->declarationMap[child];
}
}
}
qCDebug(PLUGIN_FILETEMPLATES) << declarations.size();
return declarations;
}
void OverridesPage::clear()
{
d->overriddenFunctions.clear();
overrideTree()->clear();
d->chosenOverrides.clear();
d->declarationMap.clear();
}
void OverridesPage::addBaseClasses(const QList& directBases,
const QList& allBases)
{
DUChainReadLocker lock;
foreach(const DeclarationPointer& baseClass, allBases) {
QTreeWidgetItem* classItem = new QTreeWidgetItem(overrideTree(), QStringList() << baseClass->qualifiedIdentifier().toString());
classItem->setIcon(ClassOrFunctionColumn, DUChainUtils::iconForDeclaration(baseClass.data()));
DUContext* context = baseClass->internalContext();
if (!context) {
continue;
}
//For this internal context get all the function declarations inside the class
foreach (Declaration * childDeclaration, context->localDeclarations()) {
if (AbstractFunctionDeclaration * func = dynamic_cast(childDeclaration))
{
if (func->isVirtual())
{
// Its a virtual function, add it to the list unless it's a destructor
ClassFunctionDeclaration* cFunc = dynamic_cast(childDeclaration);
if (cFunc && !cFunc->isDestructor())
{
addPotentialOverride(classItem, DeclarationPointer(childDeclaration));
}
}
else if (directBases.contains(baseClass))
{
// add ctors of direct parents
ClassFunctionDeclaration* cFunc = dynamic_cast(childDeclaration);
if (cFunc && cFunc->isConstructor())
{
addPotentialOverride(classItem, DeclarationPointer(childDeclaration));
}
}
}
}
}
overrideTree()->expandAll();
overrideTree()->header()->resizeSections(QHeaderView::ResizeToContents);
}
void OverridesPage::addPotentialOverride(QTreeWidgetItem* classItem, const DeclarationPointer& childDeclaration)
{
ClassFunctionDeclaration* function = dynamic_cast(childDeclaration.data());
if (!function) {
qCDebug(PLUGIN_FILETEMPLATES) << "Declaration is not a function:" << childDeclaration->identifier().toString();
return;
}
if (function->accessPolicy() == Declaration::Private) {
qCDebug(PLUGIN_FILETEMPLATES) << "Declaration is private, returning:" << function->identifier().toString();
return;
}
qCDebug(PLUGIN_FILETEMPLATES) << childDeclaration->toString();
if (d->overriddenFunctions.contains(childDeclaration->identifier()))
{
foreach (const DeclarationPointer& decl, d->overriddenFunctions.values(childDeclaration->identifier()))
{
if (decl->indexedType() == childDeclaration->indexedType())
{
qCDebug(PLUGIN_FILETEMPLATES) << "Declaration is already shown";
return;
}
}
}
d->overriddenFunctions.insert(childDeclaration->identifier(), childDeclaration);
QTreeWidgetItem* overrideItem = new QTreeWidgetItem(classItem, QStringList() << childDeclaration->toString());
overrideItem->setFlags( Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable) );
overrideItem->setCheckState(ClassOrFunctionColumn, d->chosenOverrides.contains(childDeclaration) ? Qt::Checked : Qt::Unchecked);
overrideItem->setIcon(ClassOrFunctionColumn, DUChainUtils::iconForDeclaration(childDeclaration.data()));
overrideItem->setData(ClassOrFunctionColumn, Qt::UserRole, QVariant::fromValue(IndexedDeclaration(childDeclaration.data())));
overrideItem->setText(AccessColumn, accessPolicyToString(function->accessPolicy()));
overrideItem->setText(PropertiesColumn, functionPropertiesToString(function));
if (function->isAbstract()) {
overrideItem->setIcon(ClassOrFunctionColumn, QIcon::fromTheme(QStringLiteral("flag-red")));
overrideItem->setCheckState(ClassOrFunctionColumn, Qt::Checked);
classItem->removeChild(overrideItem);
classItem->insertChild(0, overrideItem);
}
d->declarationMap[overrideItem] = childDeclaration;
}
QTreeWidget* OverridesPage::overrideTree() const
{
return d->overrides->overridesTree;
}
QWidget* OverridesPage::extraFunctionsContainer() const
{
return d->overrides->extraFunctionWidget;
}
void OverridesPage::selectAll()
{
for (int i = 0; i < d->overrides->overridesTree->topLevelItemCount(); ++i) {
QTreeWidgetItem* item = d->overrides->overridesTree->topLevelItem(i);
for (int j = 0; j < item->childCount(); ++j)
item->child(j)->setCheckState(ClassOrFunctionColumn, Qt::Checked);
}
}
void OverridesPage::deselectAll()
{
for (int i = 0; i < d->overrides->overridesTree->topLevelItemCount(); ++i) {
QTreeWidgetItem* item = d->overrides->overridesTree->topLevelItem(i);
for (int j = 0; j < item->childCount(); ++j)
item->child(j)->setCheckState(ClassOrFunctionColumn, Qt::Unchecked);
}
}
void OverridesPage::addCustomDeclarations (const QString& category, const QList& declarations)
{
qCDebug(PLUGIN_FILETEMPLATES) << category << declarations.size();
DUChainReadLocker lock(DUChain::lock());
QTreeWidgetItem* item = new QTreeWidgetItem(overrideTree(), QStringList() << category);
foreach (const DeclarationPointer& declaration, declarations)
{
addPotentialOverride(item, declaration);
}
overrideTree()->expandAll();
overrideTree()->header()->resizeSections(QHeaderView::ResizeToContents);
}
void OverridesPage::setFocusToFirstEditWidget()
{
d->overrides->overridesTree->setFocus();
}
diff --git a/plugins/testview/testview.cpp b/plugins/testview/testview.cpp
index a52b7cb304..e81f060bbf 100644
--- a/plugins/testview/testview.cpp
+++ b/plugins/testview/testview.cpp
@@ -1,411 +1,409 @@
/* This file is part of KDevelop
Copyright 2012 Miha Čančula
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; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "testview.h"
#include "testviewplugin.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace KDevelop;
enum CustomRoles {
ProjectRole = Qt::UserRole + 1,
SuiteRole,
CaseRole
};
TestView::TestView(TestViewPlugin* plugin, QWidget* parent)
: QWidget(parent)
, m_plugin(plugin)
, m_tree(new QTreeView(this))
, m_filter(new KRecursiveFilterProxyModel(this))
{
setWindowIcon(QIcon::fromTheme(QStringLiteral("preflight-verifier"), windowIcon()));
setWindowTitle(i18n("Unit Tests"));
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
setLayout(layout);
layout->addWidget(m_tree);
m_tree->setSortingEnabled(true);
m_tree->header()->hide();
m_tree->setIndentation(10);
m_tree->setEditTriggers(QTreeView::NoEditTriggers);
m_tree->setSelectionBehavior(QTreeView::SelectRows);
m_tree->setSelectionMode(QTreeView::SingleSelection);
m_tree->setExpandsOnDoubleClick(false);
m_tree->sortByColumn(0, Qt::AscendingOrder);
connect(m_tree, &QTreeView::activated, this, &TestView::doubleClicked);
m_model = new QStandardItemModel(this);
m_filter->setSourceModel(m_model);
m_tree->setModel(m_filter);
QAction* showSource = new QAction( QIcon::fromTheme(QStringLiteral("code-context")), i18n("Show Source"), this );
connect (showSource, &QAction::triggered, this, &TestView::showSource);
m_contextMenuActions << showSource;
addAction(plugin->actionCollection()->action(QStringLiteral("run_all_tests")));
addAction(plugin->actionCollection()->action(QStringLiteral("stop_running_tests")));
QAction* runSelected = new QAction( QIcon::fromTheme(QStringLiteral("system-run")), i18n("Run Selected Tests"), this );
connect (runSelected, &QAction::triggered, this, &TestView::runSelectedTests);
addAction(runSelected);
QLineEdit* edit = new QLineEdit(parent);
edit->setPlaceholderText(i18n("Filter..."));
edit->setClearButtonEnabled(true);
QWidgetAction* widgetAction = new QWidgetAction(this);
widgetAction->setDefaultWidget(edit);
connect(edit, &QLineEdit::textChanged, this, &TestView::changeFilter);
addAction(widgetAction);
setFocusProxy(edit);
IProjectController* pc = ICore::self()->projectController();
connect (pc, &IProjectController::projectClosed, this, &TestView::removeProject);
ITestController* tc = ICore::self()->testController();
connect (tc, &ITestController::testSuiteAdded,
this, &TestView::addTestSuite);
connect (tc, &ITestController::testSuiteRemoved,
this, &TestView::removeTestSuite);
connect (tc, &ITestController::testRunFinished,
this, &TestView::updateTestSuite);
connect (tc, &ITestController::testRunStarted,
this, &TestView::notifyTestCaseStarted);
foreach (ITestSuite* suite, tc->testSuites())
{
addTestSuite(suite);
}
}
TestView::~TestView()
{
}
void TestView::updateTestSuite(ITestSuite* suite, const TestResult& result)
{
QStandardItem* item = itemForSuite(suite);
if (!item)
{
return;
}
qCDebug(PLUGIN_TESTVIEW) << "Updating test suite" << suite->name();
item->setIcon(iconForTestResult(result.suiteResult));
for (int i = 0; i < item->rowCount(); ++i)
{
qCDebug(PLUGIN_TESTVIEW) << "Found a test case" << item->child(i)->text();
QStandardItem* caseItem = item->child(i);
if (result.testCaseResults.contains(caseItem->text()))
{
TestResult::TestCaseResult caseResult = result.testCaseResults.value(caseItem->text(), TestResult::NotRun);
caseItem->setIcon(iconForTestResult(caseResult));
}
}
}
void TestView::changeFilter(const QString &newFilter)
{
m_filter->setFilterWildcard(newFilter);
if (newFilter.isEmpty()) {
m_tree->collapseAll();
} else {
m_tree->expandAll();
}
}
void TestView::notifyTestCaseStarted(ITestSuite* suite, const QStringList& test_cases)
{
QStandardItem* item = itemForSuite(suite);
if (!item)
{
return;
}
qCDebug(PLUGIN_TESTVIEW) << "Notify a test of the suite " << suite->name() << " has started";
// Global test suite icon
item->setIcon(QIcon::fromTheme(QStringLiteral("process-idle")));
for (int i = 0; i < item->rowCount(); ++i)
{
qCDebug(PLUGIN_TESTVIEW) << "Found a test case" << item->child(i)->text();
QStandardItem* caseItem = item->child(i);
if (test_cases.contains(caseItem->text()))
{
// Each test case icon
caseItem->setIcon(QIcon::fromTheme(QStringLiteral("process-idle")));
}
}
}
QIcon TestView::iconForTestResult(TestResult::TestCaseResult result)
{
switch (result)
{
case TestResult::NotRun:
return QIcon::fromTheme(QStringLiteral("code-function"));
case TestResult::Skipped:
return QIcon::fromTheme(QStringLiteral("task-delegate"));
case TestResult::Passed:
return QIcon::fromTheme(QStringLiteral("dialog-ok-apply"));
case TestResult::UnexpectedPass:
// This is a very rare occurrence, so the icon should stand out
return QIcon::fromTheme(QStringLiteral("dialog-warning"));
case TestResult::Failed:
return QIcon::fromTheme(QStringLiteral("edit-delete"));
case TestResult::ExpectedFail:
return QIcon::fromTheme(QStringLiteral("dialog-ok"));
case TestResult::Error:
return QIcon::fromTheme(QStringLiteral("dialog-cancel"));
-
- default:
- return QIcon::fromTheme(QLatin1String(""));
}
+ Q_UNREACHABLE();
}
QStandardItem* TestView::itemForSuite(ITestSuite* suite)
{
foreach (QStandardItem* item, m_model->findItems(suite->name(), Qt::MatchRecursive))
{
if (item->parent() && item->parent()->text() == suite->project()->name()
&& !item->parent()->parent())
{
return item;
}
}
return nullptr;
}
QStandardItem* TestView::itemForProject(IProject* project)
{
QList itemsForProject = m_model->findItems(project->name());
if (!itemsForProject.isEmpty()) {
return itemsForProject.first();
}
return addProject(project);
}
void TestView::runSelectedTests()
{
QModelIndexList indexes = m_tree->selectionModel()->selectedIndexes();
if (indexes.isEmpty())
{
//if there's no selection we'll run all of them (or only the filtered)
//in case there's a filter.
const int rc = m_filter->rowCount();
indexes.reserve(rc);
for(int i=0; iindex(i, 0);
}
}
QList jobs;
ITestController* tc = ICore::self()->testController();
/*
* NOTE: If a test suite or a single test case was selected,
* the job is launched in Verbose mode with raised output window.
* If a project is selected, it is launched silently.
*
* This is the somewhat-intuitive approach. Maybe a configuration should be offered.
*/
foreach (const QModelIndex& idx, indexes)
{
QModelIndex index = m_filter->mapToSource(idx);
if (index.parent().isValid() && indexes.contains(index.parent()))
{
continue;
}
QStandardItem* item = m_model->itemFromIndex(index);
if (item->parent() == nullptr)
{
// A project was selected
IProject* project = ICore::self()->projectController()->findProjectByName(item->data(ProjectRole).toString());
foreach (ITestSuite* suite, tc->testSuitesForProject(project))
{
jobs << suite->launchAllCases(ITestSuite::Silent);
}
}
else if (item->parent()->parent() == nullptr)
{
// A suite was selected
IProject* project = ICore::self()->projectController()->findProjectByName(item->parent()->data(ProjectRole).toString());
ITestSuite* suite = tc->findTestSuite(project, item->data(SuiteRole).toString());
jobs << suite->launchAllCases(ITestSuite::Verbose);
}
else
{
// This was a single test case
IProject* project = ICore::self()->projectController()->findProjectByName(item->parent()->parent()->data(ProjectRole).toString());
ITestSuite* suite = tc->findTestSuite(project, item->parent()->data(SuiteRole).toString());
const QString testCase = item->data(CaseRole).toString();
jobs << suite->launchCase(testCase, ITestSuite::Verbose);
}
}
if (!jobs.isEmpty())
{
KDevelop::ExecuteCompositeJob* compositeJob = new KDevelop::ExecuteCompositeJob(this, jobs);
compositeJob->setObjectName(i18np("Run 1 test", "Run %1 tests", jobs.size()));
compositeJob->setProperty("test_job", true);
ICore::self()->runController()->registerJob(compositeJob);
}
}
void TestView::showSource()
{
QModelIndexList indexes = m_tree->selectionModel()->selectedIndexes();
if (indexes.isEmpty())
{
return;
}
IndexedDeclaration declaration;
ITestController* tc = ICore::self()->testController();
QModelIndex index = m_filter->mapToSource(indexes.first());
QStandardItem* item = m_model->itemFromIndex(index);
if (item->parent() == nullptr)
{
// No sense in finding source code for projects.
return;
}
else if (item->parent()->parent() == nullptr)
{
IProject* project = ICore::self()->projectController()->findProjectByName(item->parent()->data(ProjectRole).toString());
ITestSuite* suite = tc->findTestSuite(project, item->data(SuiteRole).toString());
declaration = suite->declaration();
}
else
{
IProject* project = ICore::self()->projectController()->findProjectByName(item->parent()->parent()->data(ProjectRole).toString());
ITestSuite* suite = tc->findTestSuite(project, item->parent()->data(SuiteRole).toString());
declaration = suite->caseDeclaration(item->data(CaseRole).toString());
}
DUChainReadLocker locker;
Declaration* d = declaration.data();
if (!d)
{
return;
}
QUrl url = d->url().toUrl();
KTextEditor::Cursor cursor = d->rangeInCurrentRevision().start();
locker.unlock();
IDocumentController* dc = ICore::self()->documentController();
qCDebug(PLUGIN_TESTVIEW) << "Activating declaration in" << url;
dc->openDocument(url, cursor);
}
void TestView::addTestSuite(ITestSuite* suite)
{
QStandardItem* projectItem = itemForProject(suite->project());
Q_ASSERT(projectItem);
QStandardItem* suiteItem = new QStandardItem(QIcon::fromTheme(QStringLiteral("view-list-tree")), suite->name());
suiteItem->setData(suite->name(), SuiteRole);
foreach (const QString& caseName, suite->cases())
{
QStandardItem* caseItem = new QStandardItem(iconForTestResult(TestResult::NotRun), caseName);
caseItem->setData(caseName, CaseRole);
suiteItem->appendRow(caseItem);
}
projectItem->appendRow(suiteItem);
}
void TestView::removeTestSuite(ITestSuite* suite)
{
QStandardItem* item = itemForSuite(suite);
item->parent()->removeRow(item->row());
}
QStandardItem* TestView::addProject(IProject* project)
{
QStandardItem* projectItem = new QStandardItem(QIcon::fromTheme(QStringLiteral("project-development")), project->name());
projectItem->setData(project->name(), ProjectRole);
m_model->appendRow(projectItem);
return projectItem;
}
void TestView::removeProject(IProject* project)
{
QStandardItem* projectItem = itemForProject(project);
m_model->removeRow(projectItem->row());
}
void TestView::doubleClicked(const QModelIndex& index)
{
m_tree->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
runSelectedTests();
}
QList< QAction* > TestView::contextMenuActions()
{
return m_contextMenuActions;
}