Files
2025-09-18 17:55:52 -04:00

2148 lines
69 KiB
C++

/**
* DebuggerWidgets.cpp
* Copyright (c) 2013 ROBLOX Corp. All Rights Reserved.
*/
#include "stdafx.h"
// Qt Headers
#include <QDockWidget>
#include <QToolButton>
#include <QStyledItemDelegate>
#include <QStyleOptionViewItemV4>
#include <QDesktopWidget>
#include <QScrollBar>
#include <QMouseEvent>
#include <QClipboard>
#include <QInputDialog>
#include <QComboBox>
#include <QLabel>
#include <QPainter>
// Roblox Headers
#include "util/StandardOut.h"
#include "util/ScopedAssign.h"
#include "util/BrickColor.h"
#include "util/UDim.h"
#include "script/Script.h"
#include "v8datamodel/DataModel.h"
#include "V8DataModel/ChangeHistory.h"
#include "reflection/Type.h"
// Roblox Studio Headers
#include "DebuggerWidgets.h"
#include "DebuggerClient.h"
#include "UpdateUIManager.h"
#include "RobloxMainWindow.h"
#include "QtUtilities.h"
#include "RobloxSettings.h"
#include "RobloxDocManager.h"
//TODO: Move this to generic header file
static bool populateValues(QTreeWidgetItem *pParentItem, const QString &key, const RBX::Reflection::Variant varValue);
static QTreeWidgetItem* addChildren(QTreeWidgetItem* pParentItem, int iconIndex = 6);
//--------------------------------------------------------------------------------------------
// DebuggerViewsManager
//--------------------------------------------------------------------------------------------
DebuggerViewsManager& DebuggerViewsManager::Instance()
{
static DebuggerViewsManager instance;
return instance;
}
DebuggerViewsManager::DebuggerViewsManager()
: m_bBreakpointEncountered(false)
{
//TODO: Move dockwidget initialization to UpdateUiManager.cpp
RobloxMainWindow* rbxMainWindow = &UpdateUIManager::Instance().getMainWindow();
m_DockWidgets[eDV_BREAKPOINTS] = new QDockWidget("Breakpoints", rbxMainWindow);
m_DockWidgets[eDV_BREAKPOINTS]->setObjectName("breakpointsDockWidget");
m_DockWidgets[eDV_CALLSTACK] = new QDockWidget("Call Stack", rbxMainWindow);
m_DockWidgets[eDV_CALLSTACK]->setObjectName("callStackDockWidget");
m_DockWidgets[eDV_WATCH] = new QDockWidget("Watch", rbxMainWindow);
m_DockWidgets[eDV_WATCH]->setObjectName("watchDockWidget");
m_ViewWidgets[eDV_BREAKPOINTS] = new BreakpointsWidget;
m_ViewWidgets[eDV_CALLSTACK] = new CallStackWidget;
m_ViewWidgets[eDV_WATCH] = new WatchWidget;
//set widgets
for (int dockId = 0; dockId < eDV_MAX; dockId++)
{
m_DockWidgets[(EDebuggerViewID)dockId]->setWidget(m_ViewWidgets[(EDebuggerViewID)dockId]);
}
//restore views
RobloxSettings settings;
for (int dockId = 0; dockId < eDV_MAX; dockId++)
{
if(!rbxMainWindow->restoreDockWidget(m_DockWidgets[(EDebuggerViewID)dockId]))
{
if (dockId == 0)
rbxMainWindow->addDockWidget(Qt::BottomDockWidgetArea, m_DockWidgets[(EDebuggerViewID)dockId]);
else
rbxMainWindow->tabifyDockWidget(m_DockWidgets[(EDebuggerViewID)(dockId - 1)], m_DockWidgets[(EDebuggerViewID)dockId]);
}
}
//by default hide widgets
QAction* pToggleViewAction = NULL;
QString icons[eDV_MAX];
icons[eDV_BREAKPOINTS] = QString::fromUtf8(":/16x16/images/Studio 2.0 icons/16x16/breakpoints.png");
icons[eDV_CALLSTACK] = QString::fromUtf8(":/16x16/images/Studio 2.0 icons/16x16/callStack.png");
icons[eDV_WATCH] = QString::fromUtf8(":/16x16/images/Studio 2.0 icons/16x16/watch.png");
for (int ii = 0; ii < eDV_MAX; ii++)
{
EDebuggerViewID dockId = (EDebuggerViewID)ii;
m_DockWidgets[dockId]->setVisible(false);
pToggleViewAction = m_DockWidgets[dockId]->toggleViewAction();
if (pToggleViewAction)
{
pToggleViewAction->setIcon(QPixmap(icons[dockId]));
pToggleViewAction->setIconVisibleInMenu(false);
if (rbxMainWindow->isRibbonStyle())
{
QString actionName(m_DockWidgets[dockId]->objectName()+"Action");
pToggleViewAction->setObjectName(actionName);
UpdateUIManager::defaultCommands.append(" ");
UpdateUIManager::defaultCommands.append(actionName);
rbxMainWindow->addAction(pToggleViewAction);
}
else
{
rbxMainWindow->viewToolsToolBar->addAction(pToggleViewAction);
}
}
}
//
m_cConnections.push_back(DebuggerClientManager::Instance().debuggingStarted.connect(boost::bind(&DebuggerViewsManager::onDebuggingStarted, this)));
m_cConnections.push_back(DebuggerClientManager::Instance().debuggingStopped.connect(boost::bind(&DebuggerViewsManager::onDebuggingStopped, this)));
//if debugger client get's changed (update dock widgets)
m_cConnections.push_back(DebuggerClientManager::Instance().clientActivated.connect(boost::bind(&DebuggerViewsManager::onDebuggerClientActivated, this, _1, _2)));
m_cConnections.push_back(DebuggerClientManager::Instance().clientDeactivated.connect(boost::bind(&DebuggerViewsManager::onDebuggerClientDeactivated, this)));
//update dock widgets whenever exeuction gets paused or started
m_cConnections.push_back(DebuggerClientManager::Instance().executionDataCleared.connect(boost::bind(&DebuggerViewsManager::onExecutionDataCleared, this)));
m_cConnections.push_back(DebuggerClientManager::Instance().breakpointEncountered.connect(boost::bind(&DebuggerViewsManager::onBreakpointEncountered, this, _1, _2)));
//clear all data
m_cConnections.push_back(DebuggerClientManager::Instance().clearAll.connect(boost::bind(&DebuggerViewsManager::onClearAll, this)));
//breakpoint
m_cConnections.push_back(DebuggerClientManager::Instance().breakpointAdded.connect(boost::bind(&DebuggerViewsManager::onBreakpointAdded, this, _1)));
m_cConnections.push_back(DebuggerClientManager::Instance().breakpointRemoved.connect(boost::bind(&DebuggerViewsManager::onBreakpointRemoved, this, _1)));
//watch
m_cConnections.push_back(DebuggerClientManager::Instance().watchAdded.connect(boost::bind(&DebuggerViewsManager::onWatchAdded, this, _1)));
m_cConnections.push_back(DebuggerClientManager::Instance().watchRemoved.connect(boost::bind(&DebuggerViewsManager::onWatchRemoved, this, _1)));
//debuggers paused with delay
m_cConnections.push_back(DebuggerClientManager::Instance().debuggersListUpdated.connect(boost::bind(&DebuggerViewsManager::onDebuggersListUpdated, this, _1)));
}
void DebuggerViewsManager::showDockWidget(EDebuggerViewID dockId)
{
QDockWidget* pDockWidget = m_DockWidgets[dockId];
if (pDockWidget)
{
pDockWidget->show();
pDockWidget->raise();
}
}
void DebuggerViewsManager::restoreViews()
{
//TODO: show only the last shown views
for (int dockId = 1; dockId < eDV_MAX; dockId++)
{
m_DockWidgets[(EDebuggerViewID)dockId]->show();
}
}
void DebuggerViewsManager::saveViews()
{
//TODO: save currently shown views
for (int dockId = 1; dockId < eDV_MAX; dockId++)
{
m_DockWidgets[(EDebuggerViewID)dockId]->close();
}
}
void DebuggerViewsManager::onDebuggingStarted()
{
m_bBreakpointEncountered = false;
}
void DebuggerViewsManager::onDebuggingStopped()
{
for (int dockId = 1; dockId < eDV_MAX; dockId++)
{
QMetaObject::invokeMethod(m_DockWidgets[(EDebuggerViewID)dockId], "close", Qt::QueuedConnection);
}
}
void DebuggerViewsManager::onDebuggerClientActivated(int currentBreakpoint, RBX::Scripting::ScriptDebugger::Stack currentStack)
{
getViewWidget<BreakpointsWidget>(eDV_BREAKPOINTS).setCurrentBreakpoint(currentBreakpoint);
getViewWidget<WatchWidget>(eDV_WATCH).updateWatchValues(true);
}
void DebuggerViewsManager::onDebuggerClientDeactivated()
{
// deactivate watch values
getViewWidget<WatchWidget>(eDV_WATCH).updateWatchValues(false);
}
void DebuggerViewsManager::onExecutionDataCleared()
{
getViewWidget<CallStackWidget>(eDV_CALLSTACK).clearDebuggers();
getViewWidget<CallStackWidget>(eDV_CALLSTACK).clear();
getViewWidget<BreakpointsWidget>(eDV_BREAKPOINTS).setCurrentBreakpoint(-1);
getViewWidget<WatchWidget>(eDV_WATCH).updateWatchValues(false);
}
void DebuggerViewsManager::onBreakpointEncountered(int currentBreakpointLine, DebuggerClient* pDebuggerClient)
{
if (!m_bBreakpointEncountered)
{
for (int dockId = 1; dockId < eDV_MAX; dockId++)
{
QMetaObject::invokeMethod(m_DockWidgets[(EDebuggerViewID)dockId], "show", Qt::QueuedConnection);
}
m_bBreakpointEncountered = true;
}
getViewWidget<CallStackWidget>(eDV_CALLSTACK).populateDebuggers(pDebuggerClient);
getViewWidget<BreakpointsWidget>(eDV_BREAKPOINTS).setCurrentBreakpoint(currentBreakpointLine);
getViewWidget<WatchWidget>(eDV_WATCH).updateWatchValues(true);
}
void DebuggerViewsManager::onClearAll()
{
getViewWidget<CallStackWidget>(eDV_CALLSTACK).clearDebuggers();
getViewWidget<CallStackWidget>(eDV_CALLSTACK).clear();
getViewWidget<BreakpointsWidget>(eDV_BREAKPOINTS).clear();
getViewWidget<WatchWidget>(eDV_WATCH).clear();
for (int dockId = 1; dockId < eDV_MAX; dockId++)
{
m_DockWidgets[(EDebuggerViewID)dockId]->close();
}
}
void DebuggerViewsManager::onBreakpointAdded(shared_ptr<RBX::Scripting::DebuggerBreakpoint> spBreakpoint)
{
getViewWidget<BreakpointsWidget>(eDV_BREAKPOINTS).addBreakpoint(spBreakpoint);
}
void DebuggerViewsManager::onBreakpointRemoved(shared_ptr<RBX::Scripting::DebuggerBreakpoint> spBreakpoint)
{
getViewWidget<BreakpointsWidget>(eDV_BREAKPOINTS).removeBreakpoint(spBreakpoint);
}
void DebuggerViewsManager::onWatchAdded(shared_ptr<RBX::Scripting::DebuggerWatch> spWatch)
{
getViewWidget<WatchWidget>(eDV_WATCH).addWatch(spWatch);
}
void DebuggerViewsManager::onWatchRemoved(shared_ptr<RBX::Scripting::DebuggerWatch> spWatch)
{
getViewWidget<WatchWidget>(eDV_WATCH).removeWatch(spWatch);
}
void DebuggerViewsManager::onDebuggersListUpdated(DebuggerClient* pDebuggerClient)
{
getViewWidget<CallStackWidget>(eDV_CALLSTACK).populateDebuggers(pDebuggerClient);
}
//--------------------------------------------------------------------------------------------
// NoEditDelegate
//--------------------------------------------------------------------------------------------
class NoEditDelegate: public QStyledItemDelegate
{
public:
NoEditDelegate(QObject* parent=0):QStyledItemDelegate(parent)
{;}
virtual QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{ return NULL; }
};
//--------------------------------------------------------------------------------------------
// DebuggerTreeWidget
//--------------------------------------------------------------------------------------------
DebuggerTreeWidget::DebuggerTreeWidget(QWidget* pParent)
: QTreeWidget(pParent)
{
setUniformRowHeights(true);
setRootIsDecorated(false);
setSelectionMode(QAbstractItemView::SingleSelection);
setSelectionBehavior(QAbstractItemView::SelectRows);
setItemDelegate(new NoEditDelegate(this));
connect(this, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(onItemActivated(QTreeWidgetItem*)));
connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(onItemActivated(QTreeWidgetItem*)));
connect(this, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(onItemChanged(QTreeWidgetItem*, int)));
}
void DebuggerTreeWidget::onItemActivated(QTreeWidgetItem* pItem)
{
DebuggerTreeWidgetItem* pDebuggerItem = dynamic_cast<DebuggerTreeWidgetItem*>(pItem);
if(pDebuggerItem)
pDebuggerItem->activate();
}
void DebuggerTreeWidget::onItemChanged(QTreeWidgetItem* pItem, int column)
{
DebuggerTreeWidgetItem* pDebuggerItem = dynamic_cast<DebuggerTreeWidgetItem*>(pItem);
if(pDebuggerItem)
pDebuggerItem->columnModified(column);
}
void DebuggerTreeWidget::mousePressEvent(QMouseEvent *evt)
{
if (!itemAt(evt->pos()))
setCurrentItem(NULL);
else
QTreeWidget::mousePressEvent(evt);
}
void DebuggerTreeWidget::keyPressEvent(QKeyEvent * evt)
{
if (evt->key() == Qt::Key_Delete)
{
Q_EMIT deleteSelectedItem();
evt->accept();
return;
}
QTreeWidget::keyPressEvent(evt);
}
//--------------------------------------------------------------------------------------------
// StackDetailsItem
//--------------------------------------------------------------------------------------------
StackDetailsItem::StackDetailsItem(const RBX::Scripting::ScriptDebugger::FunctionInfo& functionInfo, bool isFirstItem)
: m_bIsFirstItem(isFirstItem)
{
setData(1, Qt::DisplayRole, QVariant(functionInfo.frame));
setText(2, QString::fromStdString(functionInfo.what));
setText(3, QString::fromStdString(functionInfo.name));
setData(4, Qt::DisplayRole, QVariant(functionInfo.currentline));
setText(5, QString::fromStdString(functionInfo.short_src));
setText(6, QString::fromStdString(functionInfo.namewhat));
if (m_bIsFirstItem)
setIcon(0, QIcon(":/16x16/images/Studio 2.0 icons/16x16/debugger_arrow.png"));
m_spAssociatedScript = functionInfo.script;
}
StackDetailsItem::~StackDetailsItem()
{
DebuggerClient* pDebuggerClient = DebuggerClientManager::Instance().getDebuggerClient(m_spAssociatedScript);
if (pDebuggerClient)
pDebuggerClient->setMarker(getLine() - 1, "", false);
}
void StackDetailsItem::activate()
{
updateIcon(true);
DebuggerClient* pDebuggerClient = DebuggerClientManager::Instance().getDebuggerClient(m_spAssociatedScript);
if (pDebuggerClient)
pDebuggerClient->setCurrentFrame(getFrame());
}
void StackDetailsItem::updateIcon(bool isCurrent)
{
DebuggerClient* pDebuggerClient = DebuggerClientManager::Instance().getDebuggerClient(m_spAssociatedScript);
if (!pDebuggerClient)
return;
//do not update anything in case of it's the first frame
if (m_bIsFirstItem)
{
pDebuggerClient->setMarker(getLine() - 1, ":/16x16/images/Studio 2.0 icons/16x16/debugger_arrow.png", true);
pDebuggerClient->setCurrentLine(getLine() - 1);
return;
}
pDebuggerClient->setMarker(getLine() - 1, isCurrent ? ":/16x16/images/Studio 2.0 icons/16x16/debugger_arrow_curve_left.png" : "", true);
setIcon(0, QPixmap(isCurrent ? ":/16x16/images/Studio 2.0 icons/16x16/debugger_arrow_curve_left.png" : ""));
}
int StackDetailsItem::getLine()
{ return data(4, Qt::DisplayRole).toInt(); }
int StackDetailsItem::getFrame()
{ return data(1, Qt::DisplayRole).toInt(); }
//--------------------------------------------------------------------------------------------
// CallStackTreeWidget
//--------------------------------------------------------------------------------------------
CallStackTreeWidget::CallStackTreeWidget(QWidget* pParent)
: DebuggerTreeWidget(pParent)
, m_pPreviousItem(NULL)
{
setWindowTitle("Call Stack");
setSortingEnabled(false);
QStringList headerLabels;
headerLabels<<" "<<"Frame"<<"What"<<"Function Name"<<"Line No."<<"Source"<<"Function Explanation";
setHeaderLabels(headerLabels);
header()->setResizeMode(0,QHeaderView::Interactive);
header()->setStretchLastSection(true);
}
CallStackTreeWidget::~CallStackTreeWidget()
{
}
void CallStackTreeWidget::setCallStack(const RBX::Scripting::ScriptDebugger::Stack& stack)
{
m_pPreviousItem = NULL;
clear();
for(RBX::Scripting::ScriptDebugger::Stack::const_iterator iter = stack.begin(); iter != stack.end(); ++iter)
addTopLevelItem(new StackDetailsItem(*iter, iter==stack.begin()));
if (!stack.empty())
{
m_pPreviousItem = topLevelItem(0);
static_cast<StackDetailsItem *>(m_pPreviousItem)->updateIcon(true);
}
header()->resizeSections(QHeaderView::ResizeToContents);
}
void CallStackTreeWidget::onItemActivated(QTreeWidgetItem* pItem)
{
StackDetailsItem *pStackDetailsItem = dynamic_cast<StackDetailsItem *>(m_pPreviousItem);
if (pStackDetailsItem)
pStackDetailsItem->updateIcon(false);
DebuggerTreeWidget::onItemActivated(pItem);
m_pPreviousItem = pItem;
setCurrentItem(NULL);
}
//--------------------------------------------------------------------------------------------
// CallStackDebuggersList
//--------------------------------------------------------------------------------------------
CallStackDebuggersList::CallStackDebuggersList(QWidget* pWidget)
: QComboBox(pWidget)
, m_bIsDirty(false)
{
}
void CallStackDebuggersList::showPopup()
{
Q_EMIT popupAboutToShow();
QComboBox::showPopup();
}
//--------------------------------------------------------------------------------------------
// CallStackWidget
//--------------------------------------------------------------------------------------------
CallStackWidget::CallStackWidget(QWidget* pParent)
: m_pCurrentDebuggerClient(NULL)
{
m_pCallStackTreeWidget = new CallStackTreeWidget(this);
QLabel* pLabel = new QLabel("Current Script: ", this);
m_pScriptDebuggersList = new CallStackDebuggersList(this);
connect(m_pScriptDebuggersList, SIGNAL(popupAboutToShow()), this, SLOT(onDebuggerListPopupShow()));
//m_pScriptDebuggersList->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
m_pScriptDebuggersList->setMinimumWidth(200);
m_pScriptDebuggersList->setMaximumWidth(QWIDGETSIZE_MAX);
QSpacerItem *pHorizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
QHBoxLayout* pComboLabelLayout = new QHBoxLayout;
pComboLabelLayout->addWidget(pLabel);
pComboLabelLayout->addWidget(m_pScriptDebuggersList);
pComboLabelLayout->addItem(pHorizontalSpacer);
QGridLayout *pGridLayout = new QGridLayout(this);
pGridLayout->setSpacing(4);
pGridLayout->setContentsMargins(0, 0, 0, 0);
pGridLayout->addLayout(pComboLabelLayout, 0, 0, 1, 1);
pGridLayout->addWidget(m_pCallStackTreeWidget, 1, 0, 1, 1);
}
void CallStackWidget::populateDebuggers(DebuggerClient* pDebuggerClient)
{
// if there's no change in debugger client then mark debuggers list as dirty and return
if ( DebuggerClientManager::Instance().getCurrentDebuggerClient() &&
(m_pCurrentDebuggerClient == pDebuggerClient) &&
(m_pScriptDebuggersList->count() > 0) )
{
static_cast<CallStackDebuggersList*>(m_pScriptDebuggersList)->setDirty(true);
return;
}
QString scriptName;
DebuggerClient* currentDebuggerClient = NULL;
boost::shared_ptr<RBX::Instance> script;
bool indexSet = false;
m_pCurrentDebuggerClient = pDebuggerClient;
// first clear list widget
clearDebuggers();
clear();
// now update combo and selection of index will update list widget
const std::vector<DebuggerClient *>& debuggerClients = DebuggerClientManager::Instance().getDebuggerClients();
for (unsigned int ii=0; ii < debuggerClients.size(); ++ii)
{
currentDebuggerClient = debuggerClients[ii];
if (!currentDebuggerClient || !RBX::Scripting::DebuggerManager::singleton().findDebugger(currentDebuggerClient->getScript().get()))
continue;
script = currentDebuggerClient->getScript();
if (!script)
continue;
scriptName = script->getFullName().c_str();
const RBX::Scripting::ScriptDebugger::PausedThreads& pausedThreads = currentDebuggerClient->getPausedThreads();
for (RBX::Scripting::ScriptDebugger::PausedThreads::const_iterator it=pausedThreads.begin(); it!=pausedThreads.end(); ++it)
{
m_pScriptDebuggersList->addItem(QString("%1 (%2) - %3").arg(scriptName).arg(it->first, 1, 16).arg(it->second.pausedLine),
QVariant::fromValue<void*>((void*)script.get()));
if ((currentDebuggerClient == m_pCurrentDebuggerClient) && (currentDebuggerClient->getCurrentThread() == it->first))
{
currentDebuggerClient->setCurrentThread(currentDebuggerClient->getCurrentThread());
m_pScriptDebuggersList->setCurrentIndex(m_pScriptDebuggersList->count() - 1);
setCallStack(currentDebuggerClient->getCurrentCallStack());
indexSet = true;
}
}
}
// if index is not set then make sure we set an index
if (!indexSet && m_pScriptDebuggersList->count() > 0)
onCurrentIndexChanged(0);
connect(m_pScriptDebuggersList, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int)));
}
void CallStackWidget::clearDebuggers()
{
disconnect(m_pScriptDebuggersList, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int)));
m_pScriptDebuggersList->clear();
static_cast<CallStackDebuggersList*>(m_pScriptDebuggersList)->setDirty(false);
}
void CallStackWidget::setCallStack(const RBX::Scripting::ScriptDebugger::Stack& stack)
{ m_pCallStackTreeWidget->setCallStack(stack); }
void CallStackWidget::clear()
{ m_pCallStackTreeWidget->clear(); }
void CallStackWidget::onCurrentIndexChanged(int index)
{
QVariant var = m_pScriptDebuggersList->itemData(index);
boost::shared_ptr<RBX::Script> spScript = shared_from(static_cast<RBX::Script*>(var.value<void*>()));
if (spScript)
{
DebuggerClient* pDebuggerClient = DebuggerClientManager::Instance().getDebuggerClient(spScript);
if (pDebuggerClient)
{
// update current thread
QString text = m_pScriptDebuggersList->itemText(index);
QRegExp exp("\\((.*)\\)");
if(exp.indexIn(text) >= 0)
{
int threadID = exp.cap(1).toInt(0, 16);
pDebuggerClient->setCurrentThread(threadID);
}
if (pDebuggerClient->updateDocument())
setCallStack(pDebuggerClient->getCurrentCallStack()); // if document has been updated correctly, set current callstack
else
clear(); // else clear the previous stack details
}
}
}
void CallStackWidget::onDebuggerListPopupShow()
{
// check if we need to update debuggers list
if (!static_cast<CallStackDebuggersList*>(m_pScriptDebuggersList)->isDirty())
return;
// force debuggers list population!
clearDebuggers();
populateDebuggers(DebuggerClientManager::Instance().getCurrentDebuggerClient());
}
//--------------------------------------------------------------------------------------------
// BreakpointItem
//--------------------------------------------------------------------------------------------
BreakpointItem::BreakpointItem(boost::shared_ptr<RBX::Scripting::DebuggerBreakpoint> spBreakpoint)
: m_spBreakpoint(spBreakpoint)
, m_bIgnoreBreakpointModification(false)
, m_bIgnoreWaypointRequest(false)
{
setFlags(flags() | Qt::ItemIsUserCheckable | Qt::ItemIsEditable);
setCheckState(0, m_spBreakpoint->isEnabled() ? Qt::Checked : Qt::Unchecked);
int line = m_spBreakpoint->getLine();
setData(1, Qt::DisplayRole, QVariant(line));
RBX::Scripting::ScriptDebugger* spDebugger = RBX::Instance::fastDynamicCast<RBX::Scripting::ScriptDebugger>(spBreakpoint->getParent());
if (spDebugger)
{
setText(2, spDebugger->getScript()->getFullName().c_str());
if (DebuggerClient *pClient = DebuggerClientManager::Instance().getDebuggerClient(shared_from(spDebugger->getScript())))
setText(3, pClient->getSourceCodeAtLine(line).trimmed());
}
//setText(4, QString::fromStdString(m_spBreakpoint->getCondition()));
m_cPropertyChangedConnection = m_spBreakpoint->propertyChangedSignal.connect(boost::bind(&BreakpointItem::onBreakpointPropertyChanged, this, _1));
m_cAncestoryChangedConnection = m_spBreakpoint->ancestryChangedSignal.connect(boost::bind(&BreakpointItem::onScriptAncestryChanged, this, _1));
}
BreakpointItem::~BreakpointItem()
{
m_cPropertyChangedConnection.disconnect();
m_cAncestoryChangedConnection.disconnect();
}
bool BreakpointItem::hasBreakpoint(boost::shared_ptr<RBX::Scripting::DebuggerBreakpoint> spBreakpoint)
{ return (spBreakpoint == m_spBreakpoint); }
void BreakpointItem::columnModified(int column)
{
RBX::ScopedAssign<bool> ignoreBreakpointModificationEvent(m_bIgnoreBreakpointModification, true);
if (column == 0)
{
RBX::DataModel::LegacyLock lock(DebuggerClientManager::Instance().getDataModel(), RBX::DataModelJob::Write);
RBX::Scripting::DebuggerBreakpoint::prop_Enabled.setValue(m_spBreakpoint.get(), checkState(column) == Qt::Checked);
// update status in script doc
// since this is the only way to update enabled/disabled state of a breakpoint, so we can avoid using property signal
DebuggerClientManager::Instance().syncBreakpointState(m_spBreakpoint);
}
else if (column == 4)
{
QString modifiedCondition = text(3);
if(modifiedCondition != QString::fromStdString(m_spBreakpoint->getCondition()))
{
RBX::DataModel::LegacyLock lock(DebuggerClientManager::Instance().getDataModel(), RBX::DataModelJob::Write);
RBX::Scripting::DebuggerBreakpoint::prop_Condition.setValue(m_spBreakpoint.get(), modifiedCondition.toStdString());
}
}
}
void BreakpointItem::activate()
{
//TODO: Check if we need to highlight line with a different color?
RBX::Scripting::ScriptDebugger* pDebugger = RBX::Instance::fastDynamicCast<RBX::Scripting::ScriptDebugger>(m_spBreakpoint->getParent());
if (pDebugger)
{
RobloxDocManager::Instance().openDoc(LuaSourceBuffer::fromInstance(shared_from(pDebugger->getScript())));
DebuggerClient* pDebuggerClient = DebuggerClientManager::Instance().getDebuggerClient(shared_from(pDebugger->getScript()));
if (pDebuggerClient)
pDebuggerClient->setCurrentLine(m_spBreakpoint->getLine() - 1);
}
}
void BreakpointItem::deleteBreakpoint()
{
RBX::DataModel::LegacyLock lock(DebuggerClientManager::Instance().getDataModel(), RBX::DataModelJob::Write);
m_spBreakpoint->setParent(NULL);
m_spBreakpoint.reset();
}
void BreakpointItem::setBreakpointEnabled(bool state)
{
RBX::ScopedAssign<bool> ignoreBreakpointModificationEvent(m_bIgnoreWaypointRequest, true);
setCheckState(0, state ? Qt::Checked : Qt::Unchecked);
}
bool BreakpointItem::isBreakpointEnabled()
{ return checkState(0) == Qt::Checked; }
void BreakpointItem::onBreakpointPropertyChanged(const RBX::Reflection::PropertyDescriptor* pPropertyDescriptor)
{
if (m_bIgnoreBreakpointModification)
return;
//TODO: make sure this is called from Main Thread (issues can happen if breakpoints are set from a script)
if (*pPropertyDescriptor == RBX::Scripting::DebuggerBreakpoint::prop_Enabled)
setBreakpointEnabled(m_spBreakpoint->isEnabled());
}
void BreakpointItem::onScriptAncestryChanged(shared_ptr<RBX::Instance> newParent)
{
// update breakpoint name if the script has change in hierarchy
if (newParent)
{
RBX::Scripting::ScriptDebugger* spDebugger = RBX::Instance::fastDynamicCast<RBX::Scripting::ScriptDebugger>(m_spBreakpoint->getParent());
if (spDebugger)
setText(2, spDebugger->getScript()->getFullName().c_str());
}
}
//--------------------------------------------------------------------------------------------
// BreakpointsTreeWidget
//--------------------------------------------------------------------------------------------
BreakpointsTreeWidget::BreakpointsTreeWidget(QWidget* pParent)
: DebuggerTreeWidget(pParent)
{
//set columns
QStringList headerLabels;
headerLabels<<"Enabled"<<"Line"<<"Script Name"<<"Source Line";//<<"Condition";
setHeaderLabels(headerLabels);
header()->setResizeMode(0,QHeaderView::Interactive);
header()->setStretchLastSection(true);
//setItemDelegateForColumn(3, new QStyledItemDelegate(this));
//enable sorting
sortByColumn(2, Qt::AscendingOrder);
setSortingEnabled(true);
}
void BreakpointsTreeWidget::addBreakpoint(boost::shared_ptr<RBX::Scripting::DebuggerBreakpoint> spBreakpoint)
{
if(spBreakpoint && !findBreakpointItem(spBreakpoint))
addTopLevelItem( new BreakpointItem(spBreakpoint) );
}
void BreakpointsTreeWidget::removeBreakpoint(boost::shared_ptr<RBX::Scripting::DebuggerBreakpoint> spBreakpoint)
{
if(spBreakpoint)
{
BreakpointItem *pBPItem = findBreakpointItem(spBreakpoint);
if(pBPItem)
delete pBPItem;
}
}
void BreakpointsTreeWidget::setCurrentBreakpoint(int breakpointLine)
{
if (breakpointLine < 0)
{
clearSelection();
}
else
{
BreakpointItem *pBPItem = findBreakpointItem(breakpointLine);
if (pBPItem)
{
scrollToItem(pBPItem);
setCurrentItem(pBPItem);
}
}
}
void BreakpointsTreeWidget::deleteSelectedBreakpoint()
{
QList<QTreeWidgetItem*> selItems = selectedItems();
if (selItems.length() > 0)
{
BreakpointItem* pBreakpointItem = dynamic_cast<BreakpointItem*>(selItems[0]);
if (pBreakpointItem)
{
pBreakpointItem->deleteBreakpoint();
delete pBreakpointItem;
}
}
}
void BreakpointsTreeWidget::deleteAllBreakpoints()
{
int numChild = topLevelItemCount();
if (numChild <=0)
return;
int currentChild = 0;
BreakpointItem* pBreakpointItem = NULL;
while (currentChild < numChild)
{
pBreakpointItem = static_cast<BreakpointItem*>(topLevelItem(currentChild));
if (pBreakpointItem)
pBreakpointItem->deleteBreakpoint();
++currentChild;
}
clear();
}
void BreakpointsTreeWidget::disableEnableAllBreakpoints()
{
int numChild = topLevelItemCount();
if (numChild <=0)
return;
int currentChild = 0;
BreakpointItem* pBreakpointItem = NULL;
bool breakpointEnabled = hasBreakpointsEnabled();
while (currentChild < numChild)
{
pBreakpointItem = static_cast<BreakpointItem*>(topLevelItem(currentChild));
if (pBreakpointItem && (breakpointEnabled == pBreakpointItem->isBreakpointEnabled()))
pBreakpointItem->setBreakpointEnabled(!breakpointEnabled);
++currentChild;
}
}
void BreakpointsTreeWidget::goToSourceCode()
{
QList<QTreeWidgetItem*> selItems = selectedItems();
if (selItems.length() > 0)
{
BreakpointItem* pBreakpointItem = static_cast<BreakpointItem*>(selItems[0]);
if (pBreakpointItem)
pBreakpointItem->activate();
}
}
BreakpointItem* BreakpointsTreeWidget::findBreakpointItem(boost::shared_ptr<RBX::Scripting::DebuggerBreakpoint> spBreakpoint)
{
int numChild = topLevelItemCount(), currentChild = 0;
BreakpointItem* pBreakpointItem = NULL;
while (currentChild < numChild)
{
//static cast should be OK as we are inserting only BreakpointItem type of items!
pBreakpointItem = static_cast<BreakpointItem*>(topLevelItem(currentChild));
if (pBreakpointItem && pBreakpointItem->hasBreakpoint(spBreakpoint))
return pBreakpointItem;
++currentChild;
}
return NULL;
}
BreakpointItem* BreakpointsTreeWidget::findBreakpointItem(int breakpointLine)
{
int numChild = topLevelItemCount(), currentChild = 0;
BreakpointItem* pBreakpointItem = NULL;
while (currentChild < numChild)
{
//static cast should be OK as we are inserting only BreakpointItem type of items!
pBreakpointItem = static_cast<BreakpointItem*>(topLevelItem(currentChild));
if ( pBreakpointItem && (pBreakpointItem->data(1, Qt::DisplayRole).toInt() == breakpointLine) )
return pBreakpointItem;
++currentChild;
}
return NULL;
}
bool BreakpointsTreeWidget::hasBreakpointsEnabled()
{
int numChild = topLevelItemCount(), currentChild = 0;
BreakpointItem* pBreakpointItem = NULL;
while (currentChild < numChild)
{
//static cast should be OK as we are inserting only BreakpointItem type of items!
pBreakpointItem = static_cast<BreakpointItem*>(topLevelItem(currentChild));
if ( pBreakpointItem && pBreakpointItem->isBreakpointEnabled() )
return true;
++currentChild;
}
return false;
}
//--------------------------------------------------------------------------------------------
// BreakpointsWidget
//--------------------------------------------------------------------------------------------
BreakpointsWidget::BreakpointsWidget(QWidget* pParent)
: QWidget(pParent)
, m_bPushButtonStateUpdateRequested(false)
, m_bIgnoreBreakpointModification(false)
{
QSizePolicy pushButtonSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
pushButtonSizePolicy.setHorizontalStretch(0);
pushButtonSizePolicy.setVerticalStretch(0);
m_pDeleteBreakpointTB = new QToolButton(this);
m_pDeleteBreakpointTB->setObjectName(QString::fromUtf8("DeleteBreakpointPB"));
m_pDeleteBreakpointTB->setSizePolicy(pushButtonSizePolicy);
m_pDeleteBreakpointTB->setToolTip(tr("Delete Selected Breakpoint"));
m_pDeleteBreakpointTB->setEnabled(false);
QIcon icon;
icon.addFile(QString::fromUtf8(":/16x16/images/Studio 2.0 icons/16x16/breakpointDelete.png"), QSize(), QIcon::Normal, QIcon::Off);
m_pDeleteBreakpointTB->setIcon(icon);
QFrame *pLine1 = new QFrame(this);
pLine1->setFrameShape(QFrame::VLine);
pLine1->setFrameShadow(QFrame::Sunken);
m_pDeleteAllBreakpointsTB = new QToolButton(this);
m_pDeleteAllBreakpointsTB->setObjectName(QString::fromUtf8("DeleteAllBreakpointsPB"));
m_pDeleteAllBreakpointsTB->setSizePolicy(pushButtonSizePolicy);
m_pDeleteAllBreakpointsTB->setToolTip(tr("Delete All Breakpoints"));
QIcon icon1;
icon1.addFile(QString::fromUtf8(":/16x16/images/Studio 2.0 icons/16x16/deleteAllBreakpoints.png"), QSize(), QIcon::Normal, QIcon::Off);
m_pDeleteAllBreakpointsTB->setIcon(icon1);
m_pDeleteAllBreakpointsTB->setEnabled(false);
m_pDisableEnableAllBreakpointsTB = new QToolButton(this);
m_pDisableEnableAllBreakpointsTB->setObjectName(QString::fromUtf8("DisableEnableAllBreakpointsPB"));
m_pDisableEnableAllBreakpointsTB->setSizePolicy(pushButtonSizePolicy);
QIcon icon2;
icon2.addFile(QString::fromUtf8(":/16x16/images/Studio 2.0 icons/16x16/enableDisableBreakpoints.png"), QSize(), QIcon::Normal, QIcon::Off);
m_pDisableEnableAllBreakpointsTB->setIcon(icon2);
m_pDisableEnableAllBreakpointsTB->setEnabled(false);
QFrame *pLine2 = new QFrame(this);
pLine2->setFrameShape(QFrame::VLine);
pLine2->setFrameShadow(QFrame::Sunken);
m_pGoToSourceCodeTB = new QToolButton(this);
m_pGoToSourceCodeTB->setObjectName(QString::fromUtf8("GoToSourceCodeTB"));
m_pGoToSourceCodeTB->setSizePolicy(pushButtonSizePolicy);
QIcon icon3;
icon3.addFile(QString::fromUtf8(":/16x16/images/Studio 2.0 icons/16x16/gotoSource.png"), QSize(), QIcon::Normal, QIcon::Off);
m_pGoToSourceCodeTB->setIcon(icon3);
m_pGoToSourceCodeTB->setToolTip(tr("Go To Source Code"));
m_pGoToSourceCodeTB->setEnabled(false);
QSpacerItem *pHorizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
QHBoxLayout *pHorizontalLayout = new QHBoxLayout();
pHorizontalLayout->setSpacing(4);
pHorizontalLayout->addWidget(m_pDeleteBreakpointTB);
pHorizontalLayout->addWidget(pLine1);
pHorizontalLayout->addWidget(m_pDeleteAllBreakpointsTB);
pHorizontalLayout->addWidget(m_pDisableEnableAllBreakpointsTB);
pHorizontalLayout->addWidget(pLine2);
pHorizontalLayout->addWidget(m_pGoToSourceCodeTB);
pHorizontalLayout->addItem(pHorizontalSpacer);
m_pBreakpointTreeWidget = new BreakpointsTreeWidget(this);
QGridLayout *pGridLayout = new QGridLayout(this);
pGridLayout->setVerticalSpacing(4);
pGridLayout->setContentsMargins(0, 0, 0, 0);
pGridLayout->addLayout(pHorizontalLayout, 0, 0, 1, 1);
pGridLayout->addWidget(m_pBreakpointTreeWidget, 1, 0, 1, 1);
connect(m_pDeleteBreakpointTB, SIGNAL(clicked()), this, SLOT(onPushButtonClicked()));
connect(m_pDeleteAllBreakpointsTB, SIGNAL(clicked()), this, SLOT(onPushButtonClicked()));
connect(m_pDisableEnableAllBreakpointsTB, SIGNAL(clicked()), this, SLOT(onPushButtonClicked()));
connect(m_pGoToSourceCodeTB, SIGNAL(clicked()), this, SLOT(onPushButtonClicked()));
connect(UpdateUIManager::Instance().getMainWindow().toggleAllBreakpointsStateAction, SIGNAL(triggered()), m_pDisableEnableAllBreakpointsTB, SIGNAL(clicked()));
connect(m_pBreakpointTreeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(requestButtonsStatusUpdate()));
connect(m_pBreakpointTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(requestButtonsStatusUpdate()));
connect(m_pBreakpointTreeWidget, SIGNAL(deleteSelectedItem()), this, SLOT(onDeleteSelectedItem()));
}
void BreakpointsWidget::addBreakpoint(boost::shared_ptr<RBX::Scripting::DebuggerBreakpoint> breakpoint)
{
if (!m_bIgnoreBreakpointModification)
m_pBreakpointTreeWidget->addBreakpoint(breakpoint);
requestButtonsStatusUpdate();
}
void BreakpointsWidget::removeBreakpoint(boost::shared_ptr<RBX::Scripting::DebuggerBreakpoint> breakpoint)
{
if (!m_bIgnoreBreakpointModification)
m_pBreakpointTreeWidget->removeBreakpoint(breakpoint);
requestButtonsStatusUpdate();
}
void BreakpointsWidget::setCurrentBreakpoint(int breakpointLine)
{
m_pBreakpointTreeWidget->setCurrentBreakpoint(breakpointLine);
requestButtonsStatusUpdate();
}
void BreakpointsWidget::clear()
{
m_pBreakpointTreeWidget->clear();
requestButtonsStatusUpdate();
}
void BreakpointsWidget::onPushButtonClicked()
{
QToolButton *pButtonClicked = qobject_cast<QToolButton*>(sender());
if (!pButtonClicked)
return;
RBX::ScopedAssign<bool> ignorePropertyModifyEvent(m_bIgnoreBreakpointModification, true);
if (pButtonClicked == m_pDeleteBreakpointTB)
{
m_pBreakpointTreeWidget->deleteSelectedBreakpoint();
}
else if (pButtonClicked == m_pDeleteAllBreakpointsTB)
{
QtUtilities::RBXMessageBox msgBox;
msgBox.setIcon(QMessageBox::Question);
msgBox.setText(tr("Do you want to delete all breakpoints?"));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
if ( msgBox.exec() != QMessageBox::Yes )
return;
m_pBreakpointTreeWidget->deleteAllBreakpoints();
}
else if (pButtonClicked == m_pDisableEnableAllBreakpointsTB)
{
m_pBreakpointTreeWidget->disableEnableAllBreakpoints();
}
else if (pButtonClicked == m_pGoToSourceCodeTB)
{
m_pBreakpointTreeWidget->goToSourceCode();
}
requestButtonsStatusUpdate();
}
void BreakpointsWidget::onDeleteSelectedItem()
{
RBX::ScopedAssign<bool> ignorePropertyModifyEvent(m_bIgnoreBreakpointModification, true);
m_pBreakpointTreeWidget->deleteSelectedBreakpoint();
}
void BreakpointsWidget::requestButtonsStatusUpdate()
{
if (m_bPushButtonStateUpdateRequested)
return;
m_bPushButtonStateUpdateRequested = true;
QTimer::singleShot(0, this, SLOT(updateButtonsStatus()));
}
void BreakpointsWidget::updateButtonsStatus()
{
int numItems = m_pBreakpointTreeWidget->topLevelItemCount();
if (numItems < 1)
{
//disable all buttons
m_pDeleteBreakpointTB->setEnabled(false);
m_pDeleteAllBreakpointsTB->setEnabled(false);
m_pDisableEnableAllBreakpointsTB->setEnabled(false);
m_pGoToSourceCodeTB->setEnabled(false);
//update connected action also
UpdateUIManager::Instance().getMainWindow().toggleAllBreakpointsStateAction->setEnabled(false);
}
else
{
//enable m_pDeleteAllBreakpointsTB button if we have any item
m_pDeleteAllBreakpointsTB->setEnabled(true);
//if we have any enabled breakpoint then enable m_pDeleteAllBreakpointsTB
m_pDisableEnableAllBreakpointsTB->setEnabled(true);
if (m_pBreakpointTreeWidget->hasBreakpointsEnabled())
m_pDisableEnableAllBreakpointsTB->setToolTip(tr("Disable All Breakpoints"));
else
m_pDisableEnableAllBreakpointsTB->setToolTip(tr("Enable All Breakpoints"));
//update connected action also
UpdateUIManager::Instance().getMainWindow().toggleAllBreakpointsStateAction->setEnabled(true);
//if there are selected items, then enable m_pBreakpointTreeWidget and m_pGoToSourceCodeTB
int numSelected = m_pBreakpointTreeWidget->selectedItems().length();
m_pDeleteBreakpointTB->setEnabled(numSelected > 0);
m_pGoToSourceCodeTB->setEnabled(numSelected > 0);
}
m_bPushButtonStateUpdateRequested = false;
}
//--------------------------------------------------------------------------------------------
// WatchItem
//--------------------------------------------------------------------------------------------
WatchItem::WatchItem(boost::shared_ptr<RBX::Scripting::DebuggerWatch> spWatch)
: m_spWatch(spWatch)
, m_bIsInValid(true)
{
setFlags(flags() | Qt::ItemIsEditable);
if (m_spWatch)
{
setText(0, QString::fromStdString(m_spWatch->getCondition()));
onScriptAncestryChanged(shared_from(m_spWatch->getParent()));
m_cPropertyChangedConnection = m_spWatch->propertyChangedSignal.connect(boost::bind(&WatchItem::onWatchPropertyChanged, this, _1));
m_cAncestoryChangedConnection = m_spWatch->ancestryChangedSignal.connect(boost::bind(&WatchItem::onScriptAncestryChanged, this, _1));
}
}
WatchItem::~WatchItem()
{
m_cPropertyChangedConnection.disconnect();
m_cAncestoryChangedConnection.disconnect();
}
void WatchItem::updateWatchValue()
{
removeChildren();
if (!m_spWatch)
return;
bool result = false;
try
{
// TODO: Check if we need to get values from Active DebuggerClient or Current DebuggerClient?
RBX::Reflection::Variant Value = DebuggerClientManager::Instance().getWatchValue(m_spWatch);
if (!Value.isVoid())
result = populateValues(this, QString::fromStdString(m_spWatch->getCondition()), Value);
}
catch (std::runtime_error const& exp)
{
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "Exception: %s", exp.what());
result = false;
}
setInvalid(!result);
}
bool WatchItem::hasWatch(boost::shared_ptr<RBX::Scripting::DebuggerWatch> spWatch)
{ return m_spWatch == spWatch; }
void WatchItem::columnModified(int column)
{
if (!m_spWatch)
return;
RBX::ScopedAssign<bool> ignoreWatchModificationEvent(m_bIgnoreWatchModification, true);
if (column == 0)
{
std::string watchExpression = text(0).toStdString();
if (watchExpression != m_spWatch->getExpression())
{
//TODO: Check for syntax of expression
RBX::DataModel::LegacyLock lock(DebuggerClientManager::Instance().getDataModel(), RBX::DataModelJob::Write);
RBX::Scripting::DebuggerWatch::prop_Expression.setValue(m_spWatch.get(), watchExpression);
//also update the value
updateWatchValue();
}
}
}
void WatchItem::deleteWatch()
{
if (!m_spWatch)
return;
RBX::DataModel::LegacyLock lock(DebuggerClientManager::Instance().getDataModel(), RBX::DataModelJob::Write);
m_spWatch->setParent(NULL);
m_spWatch.reset();
}
void WatchItem::onWatchPropertyChanged(const RBX::Reflection::PropertyDescriptor* pPropertyDescriptor)
{
if (m_bIgnoreWatchModification || !m_spWatch)
return;
if (*pPropertyDescriptor == RBX::Scripting::DebuggerWatch::prop_Expression)
setText(0, QString::fromStdString(RBX::Scripting::DebuggerWatch::prop_Expression.getValue(m_spWatch.get())));
}
void WatchItem::removeChildren()
{
QList<QTreeWidgetItem *> childItems = takeChildren();
QList<QTreeWidgetItem *>::iterator iter = childItems.begin();
while (iter != childItems.end())
{
delete *iter;
++iter;
}
childItems.clear();
}
void WatchItem::setInvalid(bool state)
{
m_bIsInValid = state;
if (m_bIsInValid)
{
removeChildren();
setText(1, QString("Error symbol \"%1\" not found").arg(text(0)));
}
}
void WatchItem::onScriptAncestryChanged(shared_ptr<RBX::Instance> newParent)
{
// update script name if the script has change in hierarchy
if (newParent)
{
QString scriptName;
RBX::Scripting::ScriptDebugger* spDebugger = RBX::Instance::fastDynamicCast<RBX::Scripting::ScriptDebugger>(m_spWatch->getParent());
if (spDebugger && spDebugger->getScript())
scriptName = spDebugger->getScript()->getFullName().c_str();
setText(2, scriptName);
}
}
class DummyWatchItem: public WatchItem
{
public:
DummyWatchItem()
: WatchItem(boost::shared_ptr<RBX::Scripting::DebuggerWatch>())
, m_bIgnoreTextModified(false)
{
}
void columnModified(int column)
{
if (m_bIgnoreTextModified || column != 0)
return;
QString watchExpression = text(0);
if (!watchExpression.isEmpty())
{
// add watch
DebuggerClientManager::Instance().addWatch(watchExpression);
// remove the text entered
m_bIgnoreTextModified = true;
setText(0, "");
m_bIgnoreTextModified = false;
}
}
void setInvalid(bool state) {}
private:
bool m_bIgnoreTextModified;
};
class WatchItemDelegate : public QStyledItemDelegate
{
public:
WatchItemDelegate(WatchTreeWidget* parent)
: QStyledItemDelegate(parent)
, m_pWatchTreeWidget(parent)
{
}
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (index.column() != 0)
return QStyledItemDelegate::createEditor(parent, option, index);
// if we are in any doc other than script doc, then do not enable "DummyWatchItem"
DummyWatchItem* dummyWatchItem = dynamic_cast<DummyWatchItem*>(m_pWatchTreeWidget->itemFromIndex(index));
if (dummyWatchItem)
{
IRobloxDoc* pDoc = RobloxDocManager::Instance().getCurrentDoc();
if (pDoc && (pDoc->docType() != IRobloxDoc::SCRIPT))
{
RBX::StandardOut::singleton()->print(RBX::MESSAGE_INFO, "Please open a script document to add a new watch expression");
return NULL;
}
}
// create line edit as editor
QLineEdit *pLineEdit = new QLineEdit(parent);
pLineEdit->setGeometry(option.rect);
pLineEdit->setFrame(false);
return pLineEdit;
}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyledItemDelegate::paint(painter, option, index);
painter->save();
{
painter->setPen(QPen(QColor("lightgray")));
painter->drawLine(option.rect.right(), option.rect.y(), option.rect.right(), option.rect.bottom());
}
painter->restore();
}
private:
WatchTreeWidget* m_pWatchTreeWidget;
};
class NoEditItemDelegate : public QStyledItemDelegate
{
public:
NoEditItemDelegate(WatchTreeWidget* parent)
: QStyledItemDelegate(parent)
{
}
QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{ return NULL; }
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyledItemDelegate::paint(painter, option, index);
painter->save();
{
painter->setPen(QPen(QColor("lightgray")));
painter->drawLine(option.rect.right(), option.rect.y(), option.rect.right(), option.rect.bottom());
}
painter->restore();
}
};
//--------------------------------------------------------------------------------------------
// WatchTreeWidget
//--------------------------------------------------------------------------------------------
WatchTreeWidget::WatchTreeWidget(QWidget *pParent)
: DebuggerTreeWidget(pParent)
, m_pContextualMenu(NULL)
, m_pCopyAction(NULL)
, m_pAddWatchAction(NULL)
, m_pDeleteWatchAction(NULL)
, m_pDeleteAllWatchesAction(NULL)
, m_bIgnoreWatchModification(false)
{
//set columns
QStringList headerLabels;
headerLabels<<"Expression"<<"Value"<<"Script Name";
setHeaderLabels(headerLabels);
header()->setResizeMode(0,QHeaderView::Interactive);
header()->setResizeMode(1,QHeaderView::Interactive);
header()->setStretchLastSection(true);
header()->resizeSection(1, 200);
//expressions can be modified
setItemDelegateForColumn(0, new WatchItemDelegate(this));
//make sure we draw columns correctly, so a no edit item is required
setItemDelegateForColumn(1, new NoEditItemDelegate(this));
//required to show child expansion controls (expand/collapse)
setRootIsDecorated(true);
//add dummywatch item, to add watch inline
addDummyWatchItem();
}
void WatchTreeWidget::addWatch(boost::shared_ptr<RBX::Scripting::DebuggerWatch> spWatch)
{
if (!m_bIgnoreWatchModification)
{
if (spWatch && !findWatchItem(spWatch))
{
WatchItem* pWatchItem = new WatchItem(spWatch);
//insert item before the last item
insertTopLevelItem(topLevelItemCount() - 1, pWatchItem);
//also update the value
pWatchItem->updateWatchValue();
}
}
}
void WatchTreeWidget::removeWatch(boost::shared_ptr<RBX::Scripting::DebuggerWatch> spWatch)
{
if (!m_bIgnoreWatchModification && spWatch)
{
WatchItem *pWatchItem = findWatchItem(spWatch);
if(pWatchItem)
delete pWatchItem;
}
}
void WatchTreeWidget::updateWatchValues(bool enable)
{
try
{
int numChild = topLevelItemCount(), currentChild = 0;
WatchItem* pWatchItem = NULL;
setUpdatesEnabled(false);
while (currentChild < numChild)
{
pWatchItem = static_cast<WatchItem*>(topLevelItem(currentChild));
if (pWatchItem)
{
if (enable)
{
pWatchItem->updateWatchValue();
}
else
{
pWatchItem->setInvalid(true);
}
}
++currentChild;
}
setUpdatesEnabled(true);
}
catch (std::runtime_error const& exp)
{
setUpdatesEnabled(true);
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "Exception: %s", exp.what());
}
}
void WatchTreeWidget::deleteSelectedWatch()
{
RBX::ScopedAssign<bool> ignoreWatchDeleteEvent(m_bIgnoreWatchModification, true);
QList<QTreeWidgetItem*> selItems = selectedItems();
if (selItems.length() > 0)
{
// make sure we do not delete DummyWatchItem
WatchItem* pWatchItem = dynamic_cast<WatchItem*>(selItems[0]);
DummyWatchItem* pDummyWatchItem = dynamic_cast<DummyWatchItem*>(selItems[0]);
if (pWatchItem && !pDummyWatchItem)
{
pWatchItem->deleteWatch();
delete pWatchItem;
}
}
}
void WatchTreeWidget::copySelectedItem()
{
QList<QTreeWidgetItem*> selItems = selectedItems();
if (selItems.length() > 0)
{
QTreeWidgetItem* pWatchItem = selItems[0];
if (pWatchItem)
{
QString textToCopy;
textToCopy.append(getFullName(pWatchItem));
textToCopy.append("\t");
textToCopy.append(pWatchItem->text(1));
QClipboard *pClipboard = QApplication::clipboard();
if (pClipboard)
pClipboard->setText(textToCopy);
}
}
}
void WatchTreeWidget::addWatch()
{
QList<QTreeWidgetItem*> selItems = selectedItems();
QString watchExpression;
if (selItems.length() > 0)
{
//add watch for child
QTreeWidgetItem* pWatchItem = selItems[0];
if (pWatchItem)
{
QString fullName = getFullName(pWatchItem);
if (fullName != pWatchItem->text(0))
watchExpression = fullName;
}
}
if (!watchExpression.isEmpty())
{
DebuggerClientManager::Instance().addWatch(watchExpression);
}
else
{
DummyWatchItem* pDummyWatchItem = dynamic_cast<DummyWatchItem*>(topLevelItem(topLevelItemCount()-1));
if (pDummyWatchItem)
{
scrollToItem(pDummyWatchItem);
setCurrentIndex(indexFromItem(pDummyWatchItem, 0));
editItem(pDummyWatchItem, 0);
}
else
{
// control should not come here!
watchExpression = QInputDialog::getText(this, tr("Watch"), tr("Watch Expression:"), QLineEdit::Normal, watchExpression, NULL, Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
if (!watchExpression.isEmpty())
DebuggerClientManager::Instance().addWatch(watchExpression);
}
}
}
void WatchTreeWidget::removeWatch()
{ deleteSelectedWatch(); }
void WatchTreeWidget::deleteAllWatches()
{
int numChild = topLevelItemCount();
if (numChild <=0)
return;
RBX::ScopedAssign<bool> ignoreWatchDeleteEvent(m_bIgnoreWatchModification, true);
int currentChild = 0;
WatchItem* pWatchItem = NULL;
while (currentChild < numChild)
{
pWatchItem = dynamic_cast<WatchItem*>(topLevelItem(currentChild));
if (pWatchItem)
pWatchItem->deleteWatch();
++currentChild;
}
clear();
}
void WatchTreeWidget::contextMenuEvent(QContextMenuEvent* evt)
{
if (!m_pContextualMenu)
{
m_pContextualMenu = new QMenu(this);
m_pCopyAction = m_pContextualMenu->addAction(tr("Copy"), this, SLOT(copySelectedItem()));
m_pContextualMenu->addSeparator();
m_pAddWatchAction = m_pContextualMenu->addAction(tr("Add Watch"), this, SLOT(addWatch()));
m_pDeleteWatchAction = m_pContextualMenu->addAction(tr("Delete Watch"), this, SLOT(removeWatch()));
m_pContextualMenu->addSeparator();
m_pDeleteAllWatchesAction = m_pContextualMenu->addAction(tr("Delete All"), this, SLOT(deleteAllWatches()));
}
m_pDeleteAllWatchesAction->setEnabled(topLevelItemCount() > 0);
// allow watch addition only if we are in a script doc
IRobloxDoc *pCurrentDoc = RobloxDocManager::Instance().getCurrentDoc();
m_pAddWatchAction->setEnabled(pCurrentDoc && (pCurrentDoc->docType() == IRobloxDoc::SCRIPT));
m_pCopyAction->setEnabled(false);
m_pDeleteWatchAction->setEnabled(false);
QList<QTreeWidgetItem*> selItems = selectedItems();
if (selItems.length() == 1 && dynamic_cast<DummyWatchItem*>(selItems[0]))
{
m_pDeleteAllWatchesAction->setEnabled(true);
}
else if (selItems.length() > 0)
{
WatchItem* pWatchItem = dynamic_cast<WatchItem*>(selItems[0]);
m_pDeleteWatchAction->setEnabled(pWatchItem && !pWatchItem->parent());
m_pCopyAction->setEnabled(true);
}
m_pContextualMenu->exec(evt->globalPos());
}
void WatchTreeWidget::mousePressEvent(QMouseEvent *event)
{
DebuggerTreeWidget::mousePressEvent(event);
if ((event->button() == Qt::LeftButton) && (event->pos().x() + header()->offset() > 20) && (currentColumn() == 0))
{
QTreeWidgetItem* pTreeWidgetItem = itemAt(event->pos());
if (pTreeWidgetItem)
{
setCurrentIndex(indexFromItem(pTreeWidgetItem, 0));
editItem(pTreeWidgetItem, 0);
}
}
}
WatchItem* WatchTreeWidget::findWatchItem(boost::shared_ptr<RBX::Scripting::DebuggerWatch> spWatch)
{
int numChild = topLevelItemCount(), currentChild = 0;
WatchItem* pWatchItem = NULL;
while (currentChild < numChild)
{
//static cast should be OK as we are inserting only BreakpointItem type of items!
pWatchItem = static_cast<WatchItem*>(topLevelItem(currentChild));
if (pWatchItem && pWatchItem->hasWatch(spWatch))
return pWatchItem;
++currentChild;
}
return NULL;
}
QString WatchTreeWidget::getFullName(QTreeWidgetItem *pItem)
{
QString fullName;
while (pItem)
{
fullName.prepend(pItem->text(0));
pItem = pItem->parent();
if (pItem)
fullName.prepend('.');
}
return fullName;
}
void WatchTreeWidget::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
// For Ribbon, it get's difficult to differentiate between enabled and disabled items, make the colors darker/lighter
bool isRibbon = UpdateUIManager::Instance().getMainWindow().isRibbonStyle();
QStyleOptionViewItemV4 opt = option;
WatchItem *pItem = static_cast<WatchItem*>(itemFromIndex(index));
if (pItem && !pItem->isInvalid())
{
// we do not need to do anything for Menu mode
if (isRibbon)
{
opt.palette.setColor(QPalette::All, QPalette::Text, opt.palette.color(QPalette::Active, QPalette::Text).darker());
opt.palette.setColor(QPalette::All, QPalette::Base, opt.palette.color(QPalette::Active, QPalette::Base).darker());
}
}
else
{
QColor disabledText(opt.palette.color(QPalette::Disabled, QPalette::Text));
QColor disabledBase(opt.palette.color(QPalette::Disabled, QPalette::Base));
opt.palette.setColor(QPalette::All, QPalette::Text, isRibbon ? QColor("gray") : disabledText);
opt.palette.setColor(QPalette::All, QPalette::Base, isRibbon ? QColor("gray") : disabledBase);
}
QTreeWidget::drawRow(painter, opt, index);
painter->save();
{
painter->setPen(QPen(QColor("lightgray")));
painter->drawLine(opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom());
}
painter->restore();
}
void WatchTreeWidget::clear()
{
DebuggerTreeWidget::clear();
// add dummy item for inline watch addition
addDummyWatchItem();
}
void WatchTreeWidget::addDummyWatchItem()
{
addTopLevelItem(new DummyWatchItem);
}
//--------------------------------------------------------------------------------------------
// WatchWidget
//--------------------------------------------------------------------------------------------
WatchWidget::WatchWidget(QWidget *pParent)
: QWidget(pParent)
{
m_pWatchTreeWidget = new WatchTreeWidget(this);
QGridLayout *pGridLayout = new QGridLayout(this);
pGridLayout->setSpacing(0);
pGridLayout->setContentsMargins(0, 0, 0, 0);
pGridLayout->addWidget(m_pWatchTreeWidget, 0, 0, 1, 1);
connect(m_pWatchTreeWidget, SIGNAL(deleteSelectedItem()), this, SLOT(onDeleteSelectedItem()));
}
void WatchWidget::addWatch(shared_ptr<RBX::Scripting::DebuggerWatch> spWatch)
{ m_pWatchTreeWidget->addWatch(spWatch); }
void WatchWidget::removeWatch(shared_ptr<RBX::Scripting::DebuggerWatch> spWatch)
{ m_pWatchTreeWidget->removeWatch(spWatch); }
void WatchWidget::updateWatchValues(bool enable)
{ QMetaObject::invokeMethod(m_pWatchTreeWidget, "updateWatchValues", Qt::AutoConnection, Q_ARG(bool, enable)); }
void WatchWidget::clear()
{ m_pWatchTreeWidget->clear(); }
void WatchWidget::onDeleteSelectedItem()
{ m_pWatchTreeWidget->deleteSelectedWatch(); }
//--------------------------------------------------------------------------------------------
// DebuggerToolTipWidget
//--------------------------------------------------------------------------------------------
DebuggerToolTipWidget::DebuggerToolTipWidget(QWidget *pParent)
: QTreeWidget(pParent)
, m_pModifiedByMouse(NULL)
{
setWindowFlags(Qt::ToolTip);
setFocusPolicy(Qt::NoFocus);
setUniformRowHeights(true);
setColumnCount(2);
setRootIsDecorated(false);
setMouseTracking(true);
setStyleSheet("QTreeView::item { border-right: 1px solid black }");
header()->hide();
m_pExpansionTimer = new QTimer(this);
m_pExpansionTimer->setSingleShot(true);
connect(m_pExpansionTimer, SIGNAL(timeout()), this, SLOT(onTimeout()));
connect(this, SIGNAL(itemExpanded(QTreeWidgetItem *)), this, SLOT(computeToolTipSize()));
connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem *)), this, SLOT(computeToolTipSize()));
}
void DebuggerToolTipWidget::showToolTip(const QPoint& pos, const QString &key, const RBX::Reflection::Variant &value)
{
//clear previously set data
clear();
setRootIsDecorated(false);
//now again create the required items
QTreeWidgetItem *pTreeWidgetItem = addChildren(NULL);
try
{
populateValues(pTreeWidgetItem, key, value);
}
catch (...)
{
return;
}
m_Key = key;
if (pTreeWidgetItem->childCount() > 0)
setRootIsDecorated(true);
addTopLevelItem(pTreeWidgetItem);
computeToolTipSize();
showAt(pos);
qApp->installEventFilter(this);
}
void DebuggerToolTipWidget::hideToolTip()
{
qApp->removeEventFilter(this);
m_pExpansionTimer->stop();
m_pModifiedByMouse = NULL;
m_Key = "";
hide();
}
void DebuggerToolTipWidget::showAt(const QPoint& pos)
{
move(pos.x()+5, pos.y()+5);
show();
}
void DebuggerToolTipWidget::computeToolTipSize()
{
//calculate the required height and width
int columnsWidth = 0;
for (int ii = 0; ii < 2; ++ii)
{
resizeColumnToContents(ii);
columnsWidth += sizeHintForColumn(ii);
}
int rowsHeight = computeToolTipHeight(indexFromItem(topLevelItem(0)));
//calculate available height and width
QPoint pos(x(), y());
QRect desktopRect = QApplication::desktop()->availableGeometry(pos);
int maxWidth = desktopRect.right() - pos.x() - 5 - 5;
int maxHeight = desktopRect.bottom() - pos.y() - 5 - 5;
if (columnsWidth > maxWidth)
rowsHeight += horizontalScrollBar()->height();
//modify scrollbar appropriately
if (rowsHeight > maxHeight)
{
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
rowsHeight = maxHeight;
columnsWidth += verticalScrollBar()->width();
setAutoScroll(true);
}
else
{
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setAutoScroll(false);
}
if (columnsWidth > maxWidth)
{
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
columnsWidth = maxWidth;
}
else
{
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
setMinimumSize(columnsWidth + 5, rowsHeight);
setMaximumSize(columnsWidth + 5, rowsHeight);
}
int DebuggerToolTipWidget::computeToolTipHeight(const QModelIndex &index)
{
int s = rowHeight(index);
for (int ii = 0; ii < model()->rowCount(index); ++ii)
s += computeToolTipHeight(model()->index(ii, 0, index));
return s;
}
void DebuggerToolTipWidget::mousePressEvent(QMouseEvent * evt)
{
QPoint position = evt->pos();
QTreeWidgetItem *pItem = itemAt(position);
if (pItem)
{
int depth = getDepth(pItem);
if ((position.x() > (depth-1)*indentation()) && (position.x() < ((depth-1)*indentation()+20)))
{
m_pModifiedByMouse = pItem;
m_pExpansionTimer->start(300);
}
}
QTreeWidget::mousePressEvent(evt);
}
void DebuggerToolTipWidget::mouseMoveEvent(QMouseEvent * evt)
{
if (isVisible())
m_pExpansionTimer->start(300);
QTreeWidget::mouseMoveEvent(evt);
}
bool DebuggerToolTipWidget::eventFilter(QObject *obj, QEvent *evt)
{
switch (evt->type())
{
case QEvent::KeyRelease:
if (static_cast<QKeyEvent *>(evt)->key() == Qt::Key_Escape)
hideToolTip();
break;
case QEvent::ApplicationDeactivate:
hideToolTip();
break;
case QEvent::MouseMove:
{
QPoint mousePos = static_cast<QMouseEvent*>(evt)->globalPos();
if (!rect().contains(mapFromGlobal(mousePos)) && !parentWidget()->rect().contains(parentWidget()->mapFromGlobal(mousePos)))
hideToolTip();
break;
}
case QEvent::MouseButtonPress:
if ( (obj != this) && (obj != this->viewport()) )
hideToolTip();
break;
default:
break;
}
return QTreeWidget::eventFilter(obj, evt);
}
void DebuggerToolTipWidget::wheelEvent(QWheelEvent * e)
{
if (verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOn)
{
e->accept();
return;
}
QTreeWidget::wheelEvent(e);
}
void DebuggerToolTipWidget::onTimeout()
{
QPoint position = mapFromGlobal(QCursor::pos());
QTreeWidgetItem *pItem = itemAt(position);
if (pItem)
{
if (m_pModifiedByMouse != pItem)
{
int depth = getDepth(pItem);
if ((position.x() > (depth-1)*indentation()) && (position.x() < ((depth-1)*indentation()+20)))
{
if (!pItem->isExpanded())
expandItem(pItem);
}
m_pModifiedByMouse = NULL;
}
}
}
int DebuggerToolTipWidget::getDepth(QTreeWidgetItem *pItem)
{
int depth = 0;
while(pItem != 0)
{
depth++;
pItem = pItem->parent();
}
return depth;
}
QTreeWidgetItem* addChildren(QTreeWidgetItem* pParentItem, int iconIndex)
{
QTreeWidgetItem *pTreeWidgetItem = new QTreeWidgetItem(pParentItem);
pTreeWidgetItem->setIcon(0, QtUtilities::getPixmap(":/images/img_classtree.bmp", iconIndex, 16, true));
return pTreeWidgetItem;
}
bool populateValues(QTreeWidgetItem *pParentItem, const QString &key, const RBX::Reflection::Variant varValue)
{
bool result = true;
QString textToSet;
if(varValue.isType<boost::shared_ptr<RBX::Instance> >())
{
boost::shared_ptr<RBX::Instance> pInstance = varValue.get<boost::shared_ptr<RBX::Instance> >();
textToSet = pInstance->getName().c_str();
for (RBX::Reflection::PropertyIterator iter = pInstance->properties_begin(); iter != pInstance->properties_end(); ++iter)
{
if (RBX::Reflection::RefPropertyDescriptor::isRefPropertyDescriptor((*iter).getDescriptor()))
{
const RBX::Reflection::RefPropertyDescriptor* desc = boost::polymorphic_downcast<const RBX::Reflection::RefPropertyDescriptor*>(&(*iter).getDescriptor());
RBX::Reflection::DescribedBase* pBase = desc->getRefValue(pInstance.get());
if (pBase)
{
RBX::Instance* instance = boost::polymorphic_downcast<RBX::Instance*>(pBase);
if (instance)
{
if (!pInstance->contains(instance))
populateValues(addChildren(pParentItem), desc->name.c_str(), RBX::shared_from(instance));
}
else
{
populateValues(addChildren(pParentItem), desc->name.c_str(), std::string("NULL"));
}
}
}
else
{
if ((*iter).getDescriptor().isReadOnly() || (*iter).getDescriptor().isWriteOnly())
continue;
if (!(*iter).getDescriptor().canXmlWrite())
continue;
if (!(*iter).getDescriptor().canReplicate())
continue;
RBX::Reflection::Variant value;
(*iter).getDescriptor().getVariant(pInstance.get(), value);
populateValues(addChildren(pParentItem), (*iter).getDescriptor().name.c_str(), value);
}
}
}
else if (varValue.isType<RBX::Vector2>())
{
//get x, y
textToSet = RBX::StringConverter<RBX::Vector2>::convertToString(varValue.get<RBX::Vector2>()).c_str();
RBX::Vector2 vec2 = varValue.get<RBX::Vector2>();
populateValues(addChildren(pParentItem), "X", vec2.x);
populateValues(addChildren(pParentItem), "Y", vec2.y);
}
else if (varValue.isType<RBX::Vector2int16>())
{
//get x, y
textToSet = RBX::StringConverter<RBX::Vector2int16>::convertToString(varValue.get<RBX::Vector2int16>()).c_str();
RBX::Vector2int16 vec2 = varValue.get<RBX::Vector2int16>();
populateValues(addChildren(pParentItem), "X", int(vec2.x));
populateValues(addChildren(pParentItem), "Y", int(vec2.y));
}
else if (varValue.isType<RBX::Vector3>())
{
textToSet = RBX::StringConverter<RBX::Vector3>::convertToString(varValue.get<RBX::Vector3>()).c_str();
RBX::Vector3 vec3 = varValue.get<RBX::Vector3>();
populateValues(addChildren(pParentItem), "X", vec3.x);
populateValues(addChildren(pParentItem), "Y", vec3.y);
populateValues(addChildren(pParentItem), "Z", vec3.z);
}
else if (varValue.isType<G3D::Vector3int16>())
{
textToSet = RBX::StringConverter<G3D::Vector3int16>::convertToString(varValue.get<G3D::Vector3int16>()).c_str();
G3D::Vector3int16 vec3 = varValue.get<G3D::Vector3int16>();
populateValues(addChildren(pParentItem), "X", int(vec3.x));
populateValues(addChildren(pParentItem), "Y", int(vec3.y));
populateValues(addChildren(pParentItem), "Z", int(vec3.z));
}
else if (varValue.isType<G3D::CoordinateFrame>())
{
textToSet = RBX::StringConverter<G3D::CoordinateFrame>::convertToString(varValue.get<G3D::CoordinateFrame>()).c_str();
G3D::CoordinateFrame coordinateFrame = varValue.get<G3D::CoordinateFrame>();
populateValues(addChildren(pParentItem), "p", RBX::Reflection::Variant(coordinateFrame.translation));
populateValues(addChildren(pParentItem), "lookVector", RBX::Reflection::Variant(coordinateFrame.lookVector()));
}
else if (varValue.isType<RBX::Region3>())
{
textToSet = RBX::StringConverter<RBX::Region3>::convertToString(varValue.get<RBX::Region3>()).c_str();
RBX::Region3 region = varValue.get<RBX::Region3>();
populateValues(addChildren(pParentItem), "CFrame", RBX::Reflection::Variant(region.getCFrame()));
populateValues(addChildren(pParentItem), "Size", RBX::Reflection::Variant(region.getSize()));
}
else if (varValue.isType<RBX::Rect2D>())
{
textToSet = RBX::StringConverter<RBX::Rect2D>::convertToString(varValue.get<RBX::Rect2D>()).c_str();
RBX::Rect2D rect2D = varValue.get<RBX::Rect2D>();
populateValues(addChildren(pParentItem), "Min", RBX::Reflection::Variant(rect2D.x0y0()));
populateValues(addChildren(pParentItem), "Max", RBX::Reflection::Variant(rect2D.x1y1()));
populateValues(addChildren(pParentItem), "Width", RBX::Reflection::Variant(rect2D.width()));
populateValues(addChildren(pParentItem), "Height", RBX::Reflection::Variant(rect2D.height()));
}
else if (varValue.isType<RBX::Region3int16>())
{
textToSet = RBX::StringConverter<RBX::Region3int16>::convertToString(varValue.get<RBX::Region3int16>()).c_str();
RBX::Region3int16 region = varValue.get<RBX::Region3int16>();
populateValues(addChildren(pParentItem), "Min", RBX::Reflection::Variant(region.getMinPos()));
populateValues(addChildren(pParentItem), "Max", RBX::Reflection::Variant(region.getMaxPos()));
}
else if (varValue.isType<RBX::BrickColor>())
{
textToSet = RBX::StringConverter<RBX::BrickColor>::convertToString(varValue.get<RBX::BrickColor>()).c_str();
RBX::BrickColor brickColor = varValue.get<RBX::BrickColor>();
populateValues(addChildren(pParentItem), "Color", RBX::Reflection::Variant(brickColor.color3()));
populateValues(addChildren(pParentItem), "Name", RBX::Reflection::Variant(std::string(brickColor.name())));
populateValues(addChildren(pParentItem), "Number", RBX::Reflection::Variant((int)brickColor.number));
}
else if (varValue.isType<G3D::Color3>())
{
textToSet = RBX::StringConverter<G3D::Color3>::convertToString(varValue.get<G3D::Color3>()).c_str();
G3D::Color3 color = varValue.get<G3D::Color3>();
populateValues(addChildren(pParentItem), "r", RBX::Reflection::Variant(color.r));
populateValues(addChildren(pParentItem), "g", RBX::Reflection::Variant(color.g));
populateValues(addChildren(pParentItem), "b", RBX::Reflection::Variant(color.b));
}
else if (varValue.isType<RBX::RbxRay>())
{
textToSet = RBX::StringConverter<RBX::RbxRay>::convertToString(varValue.get<RBX::RbxRay>()).c_str();
RBX::RbxRay ray = varValue.get<RBX::RbxRay>();
populateValues(addChildren(pParentItem), "Origin", RBX::Reflection::Variant(ray.origin()));
populateValues(addChildren(pParentItem), "Direction", RBX::Reflection::Variant(ray.direction()));
}
else if (varValue.isType<RBX::UDim>())
{
textToSet = RBX::StringConverter<RBX::UDim>::convertToString(varValue.get<RBX::UDim>()).c_str();
RBX::UDim dim = varValue.get<RBX::UDim>();
populateValues(addChildren(pParentItem), "Scale", RBX::Reflection::Variant(dim.scale));
populateValues(addChildren(pParentItem), "Offset", RBX::Reflection::Variant(int(dim.offset)));
}
else if (varValue.isType<RBX::UDim2>())
{
textToSet = RBX::StringConverter<RBX::UDim2>::convertToString(varValue.get<RBX::UDim2>()).c_str();
RBX::UDim2 dim2 = varValue.get<RBX::UDim2>();
populateValues(addChildren(pParentItem), "X", RBX::Reflection::Variant(dim2.x));
populateValues(addChildren(pParentItem), "Y", RBX::Reflection::Variant(dim2.y));
}
else if (varValue.isType<shared_ptr<const RBX::Reflection::ValueMap> >())
{
textToSet = "Map";
shared_ptr<const RBX::Reflection::ValueMap> valMap = varValue.get<shared_ptr<const RBX::Reflection::ValueMap> >();
for (RBX::Reflection::ValueMap::const_iterator iter = valMap->begin(); iter != valMap->end(); ++iter)
{
populateValues(addChildren(pParentItem), QString("[%1]").arg(iter->first.c_str()), iter->second);
}
}
else if (varValue.isType<shared_ptr<const RBX::Reflection::ValueTable> >())
{
textToSet = "Table";
shared_ptr<const RBX::Reflection::ValueTable> valMap = varValue.get<shared_ptr<const RBX::Reflection::ValueTable> >();
for (RBX::Reflection::ValueTable::const_iterator iter = valMap->begin(); iter != valMap->end(); ++iter)
{
populateValues(addChildren(pParentItem), QString("[%1]").arg(iter->first.c_str()), iter->second);
}
}
else if (varValue.isType<shared_ptr<const RBX::Reflection::ValueArray> >())
{
textToSet = "Array";
shared_ptr<const RBX::Reflection::ValueArray> valMap = varValue.get<shared_ptr<const RBX::Reflection::ValueArray> >();
RBX::Reflection::ValueArray::const_iterator iter = valMap->begin();
for (int ii=1; iter != valMap->end(); ++iter, ++ii)
{
populateValues(addChildren(pParentItem), QString("[%1]").arg(ii), *iter);
}
}
else if (varValue.isType<shared_ptr<const RBX::Instances> >())
{
textToSet = "Instances";
shared_ptr<const RBX::Instances> valMap = varValue.get<shared_ptr<const RBX::Instances> >();
RBX::Instances::const_iterator iter = valMap->begin();
for (int ii=0; iter != valMap->end(); ++iter, ++ii)
{
populateValues(addChildren(pParentItem), QString::number(ii), *iter);
}
}
else if (varValue.isType<shared_ptr<const RBX::Reflection::Tuple> >())
{
textToSet = "Tuple";
shared_ptr<const RBX::Reflection::Tuple> valMap = varValue.get<shared_ptr<const RBX::Reflection::Tuple> >();
RBX::Reflection::ValueArray::const_iterator iter = valMap->values.begin();
for (int ii=0; iter != valMap->values.end(); ++iter, ++ii)
{
populateValues(addChildren(pParentItem), QString::number(ii), *iter);
}
}
else if (varValue.isType<float>() || varValue.isType<int>())
{
textToSet = QString::number(varValue.get<float>());
}
else if (varValue.isType<RBX::Lua::WeakFunctionRef>())
{
textToSet = "function";
pParentItem->setIcon(0, QtUtilities::getPixmap(":/images/img_classtree.bmp", 4, 16, true));
}
else
{
try
{
textToSet = varValue.get<std::string>().c_str();
}
catch (...)
{
result = false;
}
}
pParentItem->setText(0, key);
pParentItem->setText(1, textToSet);
return result;
}