mirror of
https://bitbucket.org/smil3y/kde-extraapps.git
synced 2025-02-26 11:52:54 +00:00
528 lines
14 KiB
C++
528 lines
14 KiB
C++
/*
|
|
* KDevelop Debugger Support
|
|
*
|
|
* Copyright 2007 Hamish Rodda <rodda@kde.org>
|
|
* Copyright 2008 Vladimir Prus <ghost@cs.msu.su>
|
|
* Copyright 2009 Niko Sams <niko.sams@gmail.com>
|
|
*
|
|
* 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 "variablecollection.h"
|
|
|
|
#include <QFont>
|
|
#include <QApplication>
|
|
|
|
#include <KLocale>
|
|
#include <KDebug>
|
|
#include <KTextEditor/TextHintInterface>
|
|
#include <KTextEditor/Document>
|
|
#include <KParts/PartManager>
|
|
|
|
#include "../../interfaces/icore.h"
|
|
#include "../../interfaces/idocumentcontroller.h"
|
|
#include "../../interfaces/iuicontroller.h"
|
|
#include "../../sublime/controller.h"
|
|
#include "../../sublime/view.h"
|
|
#include "../../interfaces/idebugcontroller.h"
|
|
#include "../interfaces/idebugsession.h"
|
|
#include "../interfaces/ivariablecontroller.h"
|
|
#include "variabletooltip.h"
|
|
#include <sublime/area.h>
|
|
|
|
namespace KDevelop {
|
|
|
|
IDebugSession* currentSession()
|
|
{
|
|
return ICore::self()->debugController()->currentSession();
|
|
}
|
|
|
|
IDebugSession::DebuggerState currentSessionState()
|
|
{
|
|
if (!currentSession()) return IDebugSession::NotStartedState;
|
|
return currentSession()->state();
|
|
}
|
|
|
|
bool hasStartedSession()
|
|
{
|
|
IDebugSession::DebuggerState s = currentSessionState();
|
|
return s != IDebugSession::NotStartedState && s != IDebugSession::EndedState;
|
|
}
|
|
|
|
Variable::Variable(TreeModel* model, TreeItem* parent,
|
|
const QString& expression,
|
|
const QString& display)
|
|
: TreeItem(model, parent),
|
|
inScope_(true), topLevel_(true), changed_(false), showError_(false), m_format(Natural)
|
|
{
|
|
expression_ = expression;
|
|
// FIXME: should not duplicate the data, instead overload 'data'
|
|
// and return expression_ directly.
|
|
if (display.isEmpty())
|
|
setData(QVector<QVariant>() << expression << QString() << QString());
|
|
else
|
|
setData(QVector<QVariant>() << display << QString() << QString());
|
|
}
|
|
|
|
QString Variable::expression() const
|
|
{
|
|
return expression_;
|
|
}
|
|
|
|
bool Variable::inScope() const
|
|
{
|
|
return inScope_;
|
|
}
|
|
|
|
void Variable::setValue(const QString& v)
|
|
{
|
|
itemData[VariableCollection::ValueColumn] = v;
|
|
reportChange();
|
|
}
|
|
|
|
QString Variable::value() const
|
|
{
|
|
return itemData[VariableCollection::ValueColumn].toString();
|
|
}
|
|
|
|
void Variable::setType(const QString& type)
|
|
{
|
|
itemData[VariableCollection::TypeColumn] = type;
|
|
reportChange();
|
|
}
|
|
|
|
QString Variable::type() const
|
|
{
|
|
return itemData[VariableCollection::TypeColumn].toString();
|
|
}
|
|
|
|
void Variable::setTopLevel(bool v)
|
|
{
|
|
topLevel_ = v;
|
|
}
|
|
|
|
void Variable::setInScope(bool v)
|
|
{
|
|
inScope_ = v;
|
|
for (int i=0; i < childCount(); ++i) {
|
|
if (Variable *var = dynamic_cast<Variable*>(child(i))) {
|
|
var->setInScope(v);
|
|
}
|
|
}
|
|
reportChange();
|
|
}
|
|
|
|
void Variable::setShowError (bool v)
|
|
{
|
|
showError_ = v;
|
|
reportChange();
|
|
}
|
|
|
|
bool Variable::showError()
|
|
{
|
|
return showError_;
|
|
}
|
|
|
|
|
|
Variable::~Variable()
|
|
{
|
|
}
|
|
|
|
void Variable::die()
|
|
{
|
|
removeSelf();
|
|
deleteLater();
|
|
}
|
|
|
|
|
|
void Variable::setChanged(bool c)
|
|
{
|
|
changed_=c;
|
|
reportChange();
|
|
}
|
|
|
|
void Variable::resetChanged()
|
|
{
|
|
setChanged(false);
|
|
for (int i=0; i<childCount(); ++i) {
|
|
TreeItem* childItem = child(i);
|
|
if (dynamic_cast<Variable*>(childItem)) {
|
|
static_cast<Variable*>(childItem)->resetChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
Variable::format_t Variable::str2format(const QString& str)
|
|
{
|
|
if(str=="Binary" || str=="binary") return Binary;
|
|
if(str=="Octal" || str=="octal") return Octal;
|
|
if(str=="Decimal" || str=="decimal") return Decimal;
|
|
if(str=="Hexadecimal" || str=="hexadecimal")return Hexadecimal;
|
|
|
|
return Natural; // maybe most reasonable default
|
|
}
|
|
|
|
QString Variable::format2str(format_t format)
|
|
{
|
|
switch(format) {
|
|
case Natural: return "natural";
|
|
case Binary: return "binary";
|
|
case Octal: return "octal";
|
|
case Decimal: return "decimal";
|
|
case Hexadecimal: return "hexadecimal";
|
|
default: return "";
|
|
}
|
|
}
|
|
|
|
|
|
void Variable::setFormat(Variable::format_t format)
|
|
{
|
|
if (m_format != format) {
|
|
m_format = format;
|
|
formatChanged();
|
|
}
|
|
}
|
|
|
|
void Variable::formatChanged()
|
|
{
|
|
}
|
|
|
|
QVariant Variable::data(int column, int role) const
|
|
{
|
|
if (showError_) {
|
|
if (role == Qt::FontRole) {
|
|
QVariant ret = TreeItem::data(column, role);
|
|
QFont font = ret.value<QFont>();
|
|
font.setStyle(QFont::StyleItalic);
|
|
return font;
|
|
} else if (column == 1 && role == Qt::DisplayRole) {
|
|
return i18n("Error");
|
|
}
|
|
}
|
|
if (column == 1 && role == Qt::ForegroundRole)
|
|
{
|
|
// FIXME: returning hardcoded gray is bad,
|
|
// but we don't have access to any widget, or pallette
|
|
// thereof, at this point.
|
|
if(!inScope_) return QColor(128, 128, 128);
|
|
if(changed_) return QColor(255, 0, 0);
|
|
}
|
|
if (role == Qt::ToolTipRole) {
|
|
return TreeItem::data(column, Qt::DisplayRole);
|
|
}
|
|
|
|
return TreeItem::data(column, role);
|
|
}
|
|
|
|
Watches::Watches(TreeModel* model, TreeItem* parent)
|
|
: TreeItem(model, parent), finishResult_(0)
|
|
{
|
|
setData(QVector<QVariant>() << i18n("Auto") << QString());
|
|
}
|
|
|
|
Variable* Watches::add(const QString& expression)
|
|
{
|
|
if (!hasStartedSession()) return 0;
|
|
|
|
Variable* v = currentSession()->variableController()->createVariable(
|
|
model(), this, expression);
|
|
appendChild(v);
|
|
v->attachMaybe();
|
|
if (childCount() == 1 && !isExpanded()) {
|
|
setExpanded(true);
|
|
}
|
|
return v;
|
|
}
|
|
|
|
Variable *Watches::addFinishResult(const QString& convenienceVarible)
|
|
{
|
|
if( finishResult_ )
|
|
{
|
|
removeFinishResult();
|
|
}
|
|
finishResult_ = currentSession()->variableController()->createVariable(
|
|
model(), this, convenienceVarible, "$ret");
|
|
appendChild(finishResult_);
|
|
finishResult_->attachMaybe();
|
|
if (childCount() == 1 && !isExpanded()) {
|
|
setExpanded(true);
|
|
}
|
|
return finishResult_;
|
|
}
|
|
|
|
void Watches::removeFinishResult()
|
|
{
|
|
if (finishResult_)
|
|
{
|
|
finishResult_->die();
|
|
finishResult_ = 0;
|
|
}
|
|
}
|
|
|
|
void Watches::resetChanged()
|
|
{
|
|
for (int i=0; i<childCount(); ++i) {
|
|
TreeItem* childItem = child(i);
|
|
if (dynamic_cast<Variable*>(childItem)) {
|
|
static_cast<Variable*>(childItem)->resetChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
QVariant Watches::data(int column, int role) const
|
|
{
|
|
#if 0
|
|
if (column == 0 && role == Qt::FontRole)
|
|
{
|
|
/* FIXME: is creating font again and agian efficient? */
|
|
QFont f = font();
|
|
f.setBold(true);
|
|
return f;
|
|
}
|
|
#endif
|
|
return TreeItem::data(column, role);
|
|
}
|
|
|
|
void Watches::reinstall()
|
|
{
|
|
for (int i = 0; i < childItems.size(); ++i)
|
|
{
|
|
Variable* v = static_cast<Variable*>(child(i));
|
|
v->attachMaybe();
|
|
}
|
|
}
|
|
|
|
Locals::Locals(TreeModel* model, TreeItem* parent, const QString &name)
|
|
: TreeItem(model, parent)
|
|
{
|
|
setData(QVector<QVariant>() << name << QString());
|
|
}
|
|
|
|
QList<Variable*> Locals::updateLocals(QStringList locals)
|
|
{
|
|
QSet<QString> existing, current;
|
|
for (int i = 0; i < childItems.size(); i++)
|
|
{
|
|
Q_ASSERT(dynamic_cast<KDevelop::Variable*>(child(i)));
|
|
Variable* var= static_cast<KDevelop::Variable*>(child(i));
|
|
existing << var->expression();
|
|
}
|
|
|
|
foreach (const QString& var, locals) {
|
|
current << var;
|
|
// If we currently don't display this local var, add it.
|
|
if( !existing.contains( var ) ) {
|
|
// FIXME: passing variableCollection this way is awkward.
|
|
// In future, variableCollection probably should get a
|
|
// method to create variable.
|
|
Variable* v =
|
|
currentSession()->variableController()->createVariable(
|
|
ICore::self()->debugController()->variableCollection(),
|
|
this, var );
|
|
appendChild( v, false );
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < childItems.size(); ++i) {
|
|
KDevelop::Variable* v = static_cast<KDevelop::Variable*>(child(i));
|
|
if (!current.contains(v->expression())) {
|
|
removeChild(i);
|
|
--i;
|
|
// FIXME: check that -var-delete is sent.
|
|
delete v;
|
|
}
|
|
}
|
|
|
|
|
|
if (hasMore()) {
|
|
setHasMore(false);
|
|
}
|
|
|
|
// Variables which changed just value are updated by a call to -var-update.
|
|
// Variables that changed type -- likewise.
|
|
|
|
QList<Variable*> ret;
|
|
foreach (TreeItem *i, childItems) {
|
|
Q_ASSERT(dynamic_cast<Variable*>(i));
|
|
ret << static_cast<Variable*>(i);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void Locals::resetChanged()
|
|
{
|
|
for (int i=0; i<childCount(); ++i) {
|
|
TreeItem* childItem = child(i);
|
|
if (dynamic_cast<Variable*>(childItem)) {
|
|
static_cast<Variable*>(childItem)->resetChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
VariablesRoot::VariablesRoot(TreeModel* model)
|
|
: TreeItem(model)
|
|
{
|
|
watches_ = new Watches(model, this);
|
|
appendChild(watches_, true);
|
|
}
|
|
|
|
|
|
Locals* VariablesRoot::locals(const QString& name)
|
|
{
|
|
if (!locals_.contains(name)) {
|
|
locals_[name] = new Locals(model(), this, name);
|
|
appendChild(locals_[name]);
|
|
}
|
|
return locals_[name];
|
|
}
|
|
|
|
QHash<QString, Locals*> VariablesRoot::allLocals() const
|
|
{
|
|
return locals_;
|
|
}
|
|
|
|
void VariablesRoot::resetChanged()
|
|
{
|
|
watches_->resetChanged();
|
|
foreach (Locals *l, locals_) {
|
|
l->resetChanged();
|
|
}
|
|
}
|
|
|
|
VariableCollection::VariableCollection(IDebugController* controller)
|
|
: TreeModel(QVector<QString>() << i18n( "Name" ) << i18n( "Value" ) << i18n("Type"), controller), m_widgetVisible(false)
|
|
{
|
|
universe_ = new VariablesRoot(this);
|
|
setRootItem(universe_);
|
|
|
|
//new ModelTest(this);
|
|
|
|
connect (ICore::self()->documentController(),
|
|
SIGNAL(textDocumentCreated(KDevelop::IDocument*)),
|
|
this,
|
|
SLOT(textDocumentCreated(KDevelop::IDocument*)) );
|
|
|
|
connect(controller, SIGNAL(currentSessionChanged(KDevelop::IDebugSession*)),
|
|
SLOT(updateAutoUpdate(KDevelop::IDebugSession*)));
|
|
|
|
connect(locals(), SIGNAL(expanded()), SLOT(updateAutoUpdate()));
|
|
connect(locals(), SIGNAL(collapsed()), SLOT(updateAutoUpdate()));
|
|
connect(watches(), SIGNAL(expanded()), SLOT(updateAutoUpdate()));
|
|
connect(watches(), SIGNAL(collapsed()), SLOT(updateAutoUpdate()));
|
|
}
|
|
|
|
void VariableCollection::variableWidgetHidden()
|
|
{
|
|
m_widgetVisible = false;
|
|
updateAutoUpdate();
|
|
}
|
|
|
|
void VariableCollection::variableWidgetShown()
|
|
{
|
|
m_widgetVisible = true;
|
|
updateAutoUpdate();
|
|
}
|
|
|
|
void VariableCollection::updateAutoUpdate(IDebugSession* session)
|
|
{
|
|
if (!session) session = currentSession();
|
|
kDebug() << session;
|
|
if (!session) return;
|
|
|
|
if (!m_widgetVisible) {
|
|
session->variableController()->setAutoUpdate(IVariableController::UpdateNone);
|
|
} else {
|
|
QFlags<IVariableController::UpdateType> t = IVariableController::UpdateNone;
|
|
if (locals()->isExpanded()) t |= IVariableController::UpdateLocals;
|
|
if (watches()->isExpanded()) t |= IVariableController::UpdateWatches;
|
|
session->variableController()->setAutoUpdate(t);
|
|
}
|
|
}
|
|
|
|
VariableCollection::~ VariableCollection()
|
|
{
|
|
}
|
|
|
|
void VariableCollection::textDocumentCreated(IDocument* doc)
|
|
{
|
|
connect( doc->textDocument(),
|
|
SIGNAL(viewCreated(KTextEditor::Document*,KTextEditor::View*)),
|
|
this, SLOT(viewCreated(KTextEditor::Document*,KTextEditor::View*)) );
|
|
|
|
foreach( KTextEditor::View* view, doc->textDocument()->views() )
|
|
viewCreated( doc->textDocument(), view );
|
|
}
|
|
|
|
void VariableCollection::viewCreated(KTextEditor::Document* doc,
|
|
KTextEditor::View* view)
|
|
{
|
|
Q_UNUSED(doc);
|
|
using namespace KTextEditor;
|
|
TextHintInterface *iface = dynamic_cast<TextHintInterface*>(view);
|
|
if( !iface )
|
|
return;
|
|
|
|
iface->enableTextHints(500);
|
|
|
|
connect(view,
|
|
SIGNAL(needTextHint(KTextEditor::Cursor,QString&)),
|
|
this,
|
|
SLOT(textHintRequested(KTextEditor::Cursor,QString&)));
|
|
}
|
|
|
|
void VariableCollection::
|
|
textHintRequested(const KTextEditor::Cursor& cursor, QString&)
|
|
{
|
|
// Don't do anything if there's already an open tooltip.
|
|
if (activeTooltip_)
|
|
return;
|
|
|
|
if (!hasStartedSession())
|
|
return;
|
|
|
|
if (ICore::self()->uiController()->activeArea()->objectName() != "debug")
|
|
return;
|
|
|
|
//TODO: These keyboardModifiers should also hide already opened tooltip, and show another one for code area.
|
|
if (QApplication::keyboardModifiers() == Qt::ControlModifier ||
|
|
QApplication::keyboardModifiers() == Qt::AltModifier){
|
|
return;
|
|
}
|
|
|
|
// Figure what is the parent widget and what is the text to show
|
|
KTextEditor::View* view = dynamic_cast<KTextEditor::View*>(sender());
|
|
if (!view)
|
|
return;
|
|
|
|
KTextEditor::Document* doc = view->document();
|
|
|
|
QString expression = currentSession()->variableController()->expressionUnderCursor(doc, cursor);
|
|
|
|
if (expression.isEmpty())
|
|
return;
|
|
|
|
QPoint local = view->cursorToCoordinate(cursor);
|
|
QPoint global = view->mapToGlobal(local);
|
|
QWidget* w = view->childAt(local);
|
|
if (!w)
|
|
w = view;
|
|
|
|
activeTooltip_ = new VariableToolTip(w, global+QPoint(30,30), expression);
|
|
}
|
|
|
|
}
|
|
|
|
#include "moc_variablecollection.cpp"
|