kde-extraapps/kdevplatform/debugger/variable/variablecollection.cpp

529 lines
14 KiB
C++
Raw Normal View History

/*
* 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"