You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/Tools/LuaIDE/Source/LUA/WatchesPanel.cpp

875 lines
26 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "WatchesPanel.hxx"
#include <Source/LUA/moc_WatchesPanel.cpp>
#include "LUAEditorDebuggerMessages.h"
#include <AzCore/Script/lua/lua.h>
#include <QSortFilterProxyModel>
#include <QMenu>
#include <QAction>
#include <QKeyEvent>
namespace WatchesPanel
{
const char* typeStringLUT[] =
{
"NIL",
"BOOLEAN",
"LIGHTUSERDATA",
"NUMBER",
"STRING",
"TABLE",
"FUNCTION",
"USERDATA",
"THREAD",
NULL
};
}
class WatchesFilterModel
: public QSortFilterProxyModel
{
public:
AZ_CLASS_ALLOCATOR(WatchesFilterModel, AZ::SystemAllocator, 0);
WatchesFilterModel(QObject* pParent)
: QSortFilterProxyModel(pParent)
{
setFilterCaseSensitivity(Qt::CaseSensitive);
setDynamicSortFilter(false);
}
protected:
virtual bool lessThan(const QModelIndex& left, const QModelIndex& right) const
{
return QSortFilterProxyModel::lessThan(left, right);
}
};
DHWatchesWidget::DHWatchesWidget(QWidget* parent)
: AzToolsFramework::QTreeViewWithStateSaving(parent)
{
setFocusPolicy(Qt::StrongFocus); // required for key press handling
setEnabled(false);
setSortingEnabled(true);
sortByColumn(0, Qt::AscendingOrder);
SetOperatingMode(WATCHES_MODE_GENERAL);
m_OperatingState = WATCHES_STATE_DISCONNECTED;
m_pFilterModel = aznew WatchesFilterModel(this);
m_pFilterModel->setSourceModel(&m_DM);
setModel(m_pFilterModel);
LUAEditor::LUAWatchesDebuggerMessages::Handler::BusConnect();
LUAEditor::LUALocalsTrackerMessages::Handler::BusConnect();
LUAEditor::LUABreakpointTrackerMessages::Handler::BusConnect();
connect(&m_DM, SIGNAL(dataChanged (const QModelIndex &, const QModelIndex &)), this, SLOT(OnItemChanged()));
connect(&m_DM, SIGNAL(modelReset()), this, SLOT(OnItemChanged()));
connect(this, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(OnDoubleClicked(const QModelIndex &)));
auto crc = AZ::Crc32("StandaloneToolsWatchesPanel");
InitializeTreeViewSaving(crc);
ForceSelectNewWatch();
}
DHWatchesWidget::~DHWatchesWidget()
{
LUAEditor::LUABreakpointTrackerMessages::Handler::BusDisconnect();
LUAEditor::LUALocalsTrackerMessages::Handler::BusDisconnect();
LUAEditor::LUAWatchesDebuggerMessages::Handler::BusDisconnect();
}
void DHWatchesWidget::SetOperatingMode(WatchesOperatingMode newMode)
{
m_OperatingMode = newMode;
m_DM.SetOperatingMode(newMode);
}
void DHWatchesWidget::OnDebuggerAttached()
{
m_OperatingState = WATCHES_STATE_CONNECTED;
setEnabled(true);
if (m_OperatingMode == WATCHES_MODE_GENERAL)
{
CaptureVariables();
}
}
void DHWatchesWidget::OnDebuggerDetached()
{
m_OperatingState = WATCHES_STATE_DISCONNECTED;
LocalsClear();
setEnabled(false);
}
void DHWatchesWidget::CaptureVariables()
{
//AZ_TracePrintf("LUA Debug", "CaptureVariables:\n");
if (m_OperatingState == WATCHES_STATE_CONNECTED)
{
// query watched variables
int shortLoop = m_OperatingMode == WATCHES_MODE_GENERAL ? 1 : 0;
int rowCount = m_DM.rowCount() - shortLoop; // negative offset to skip the <new watch> for the not-locals panel
//AZ_TracePrintf("LUA Debug", "CaptureVariables( count = %d)\n",rowCount);
for (int i = 0; i < rowCount; ++i)
{
QModelIndex indexChild = m_DM.index(i, 0);
if (indexChild.isValid())
{
QString name;
name = m_DM.data(indexChild).toString();
//AZ_TracePrintf("LUA Editor", " - RequestWatchedVariable( %s )\n", AZStd::string(name.toAscii()));
EBUS_EVENT(LUAEditor::LUAWatchesRequestMessages::Bus, RequestWatchedVariable, AZStd::string(name.toUtf8().data()));
// results will return via WatchesUpdate() values asynchronously
// NB: not recursive, only top-level variable names are requested
}
}
}
}
void DHWatchesWidget::BreakpointHit(const LUAEditor::Breakpoint& bp)
{
(void)bp;
if (m_OperatingMode == WATCHES_MODE_GENERAL)
{
CaptureVariables();
}
else // m_OperatingMode == WATCHES_MODE_LOCALS
{
if (isVisible())
{
EBUS_EVENT(LUAEditor::LUAEditorDebuggerMessages::Bus, EnumLocals);
}
}
}
void DHWatchesWidget::WatchesUpdate(const AZ::ScriptContextDebug::DebugValue& topmostDebugReference)
{
//AZ_TracePrintf("LUA Editor", "incoming WatchesUpdate( %s )\n", topmostDebugReference.m_name);
disconnect(&m_DM, SIGNAL(dataChanged (const QModelIndex &, const QModelIndex &)), this, SLOT(OnItemChanged()));
disconnect(&m_DM, SIGNAL(modelReset()), this, SLOT(OnItemChanged()));
m_DM.UpdateMatchingDVs(topmostDebugReference);
connect(&m_DM, SIGNAL(dataChanged (const QModelIndex &, const QModelIndex &)), this, SLOT(OnItemChanged()));
connect(&m_DM, SIGNAL(modelReset()), this, SLOT(OnItemChanged()));
ApplyTreeViewSnapshot();
}
void DHWatchesWidget::OnItemChanged()
{
if (m_OperatingMode == WATCHES_MODE_GENERAL)
{
CaptureVariables();
}
}
void DHWatchesWidget::OnDoubleClicked(const QModelIndex& index)
{
//AZ_TracePrintf("BP", "OnDoubleClicked() %d, %d\n", index.row(), index.column());
if (m_OperatingState == WATCHES_STATE_CONNECTED)
{
if (m_pFilterModel->mapToSource(index).column() == 2)
{
// double click popup only works on the TYPE column
{
if (m_DM.IsTypeChangeAllowed(m_pFilterModel->mapToSource(index)))
{
QMenu* popupMenu = new QMenu(this);
{
QMenu* layoutMenu = new QMenu(tr("LUA Value Type"), this);
layoutMenu->addAction(tr("Boolean"));
layoutMenu->addAction(tr("Number"));
layoutMenu->addAction(tr("String"));
popupMenu->addMenu(layoutMenu);
QAction* act = popupMenu->exec(QCursor::pos());
if (act)
{
char newType = LUA_TNONE;
if (act->text() == tr("Boolean"))
{
newType = LUA_TBOOLEAN;
}
if (act->text() == tr("Number"))
{
newType = LUA_TNUMBER;
}
if (act->text() == tr("String"))
{
newType = LUA_TSTRING;
}
m_DM.SetType(m_pFilterModel->mapToSource(index), newType);
}
}
}
}
}
}
}
void DHWatchesWidget::LocalsUpdate(const AZStd::vector<AZStd::string>& vars)
{
disconnect(&m_DM, SIGNAL(dataChanged (const QModelIndex &, const QModelIndex &)), this, SLOT(OnItemChanged()));
disconnect(&m_DM, SIGNAL(modelReset()), this, SLOT(OnItemChanged()));
if (m_OperatingMode == WATCHES_MODE_LOCALS)
{
LocalsClear();
//AZ_TracePrintf("LUA Editor", "LOCALSUPDATE\n");
for (AZStd::vector<AZStd::string>::const_iterator it = vars.begin(); it != vars.end(); ++it)
{
//AZ_TracePrintf("LUA Editor", " - LocalsUpdate( %s )\n", it->c_str());
m_DM.AddWatch(it->c_str());
}
}
connect(&m_DM, SIGNAL(dataChanged (const QModelIndex &, const QModelIndex &)), this, SLOT(OnItemChanged()));
connect(&m_DM, SIGNAL(modelReset()), this, SLOT(OnItemChanged()));
}
void DHWatchesWidget::LocalsClear()
{
//AZ_TracePrintf("LUA Editor", "LOCALS Clear\n");
disconnect(&m_DM, SIGNAL(dataChanged (const QModelIndex &, const QModelIndex &)), this, SLOT(OnItemChanged()));
disconnect(&m_DM, SIGNAL(modelReset()), this, SLOT(OnItemChanged()));
if (m_OperatingMode == WATCHES_MODE_LOCALS)
{
if (m_DM.rowCount())
{
// local implementation of removeRows handles the begin/end cycle
m_DM.removeRows(0, m_DM.rowCount());
}
}
connect(&m_DM, SIGNAL(modelReset()), this, SLOT(OnItemChanged()));
connect(&m_DM, SIGNAL(dataChanged (const QModelIndex &, const QModelIndex &)), this, SLOT(OnItemChanged()));
}
void DHWatchesWidget::keyPressEvent(QKeyEvent* event)
{
if (m_OperatingMode == WATCHES_MODE_GENERAL)
{
if (event->isAccepted())
{
if (event->key() == Qt::Key_Delete)
{
QModelIndexList mil = selectedIndexes();
for (QModelIndexList::iterator iter = mil.begin(); iter != mil.end(); ++iter)
{
QModelIndex toRemove = m_pFilterModel->mapToSource(*iter);
m_DM.RemoveWatch(toRemove);
}
event->accept();
return;
}
if ((event->key() == Qt::Key_Enter) || (event->key() == Qt::Key_Return))
{
QModelIndexList mil = selectedIndexes();
if (mil.size())
{
// edit the selected item by spoofing QT's "edit key"
QKeyEvent evt(event->type(), Qt::Key_F2, event->modifiers());
QTreeView::keyPressEvent(&evt);
event->accept();
return;
}
else
{
// if nothing is selected then force edit to the <new watch> item
ForceSelectNewWatch();
event->ignore();
QTreeView::keyPressEvent(event);
return;
}
}
}
}
event->ignore();
QTreeView::keyPressEvent(event);
}
void DHWatchesWidget::ForceSelectNewWatch()
{
if (m_OperatingMode == WATCHES_MODE_GENERAL)
{
int row = m_DM.rowCount() - 1;
QModelIndex index = m_DM.index(row, 0);
selectionModel()->select(m_pFilterModel->mapFromSource(index), QItemSelectionModel::ClearAndSelect);
setCurrentIndex(m_pFilterModel->mapFromSource(index));
}
}
//------------------------------------------------------------------------
WatchesDataModel::WatchesDataModel()
{
m_parentsDirty = false;
m_OperatingMode = WATCHES_MODE_GENERAL;
}
WatchesDataModel::~WatchesDataModel()
{
}
void WatchesDataModel::SetOperatingMode(WatchesOperatingMode newMode)
{
beginResetModel(); // this has to be a reset because changing the operating mode can change row count.
m_OperatingMode = newMode;
endResetModel();
}
void WatchesDataModel::AddWatch(const AZ::ScriptContextDebug::DebugValue& newData)
{
//beginResetModel(); // this has to be a reset because push_back can reallocate.
beginInsertRows(QModelIndex(), (int)m_DebugValues.size(), (int)m_DebugValues.size());
//AZ_TracePrintf("LUA Editor", "AddWatch( lr = %d rc = %d dvsize = %d )\n",m_OperatingMode,rowCount(),m_DebugValues.size());
m_DebugValues.push_back(newData);
m_parentsDirty = true;
endInsertRows();
}
void WatchesDataModel::AddWatch(AZStd::string newName)
{
//AZ_TracePrintf("LUA Editor", "AddWatch( lr = %d - %s )\n",m_OperatingMode,newName.c_str());
AZ::ScriptContextDebug::DebugValue dv;
dv.m_name = newName;
dv.m_value = "<invalid>";
dv.m_type = LUA_TNONE;
dv.m_typeId = 0;
dv.m_flags = 0;
AddWatch(dv);
}
void WatchesDataModel::RemoveWatch(QModelIndex& index)
{
if (IsRealIndex(index))
{
const QModelIndex qmi = GetTopmostIndex(index);
// we only delete full rows, incoming selections can hold many columns in that row
// and so we must skip any but the first column
if (qmi.isValid() && (qmi.column() == 0))
{
removeRow(qmi.row(), parent(qmi));
}
}
}
bool WatchesDataModel::removeRows (int row, int count, const QModelIndex& parent)
{
(void)count;
beginRemoveRows(parent, row, row + count - 1);
m_DebugValues.erase(m_DebugValues.begin() + row, m_DebugValues.begin() + row + count);
m_parentsDirty = true;
endRemoveRows();
return true;
}
const AZ::ScriptContextDebug::DebugValue *WatchesDataModel::GetDV(const QModelIndex & index) const
{
if (IsRealIndex(index))
{
const AZ::ScriptContextDebug::DebugValue *dv = static_cast<const AZ::ScriptContextDebug::DebugValue *>(index.internalPointer());
return dv;
}
return NULL;
}
const QModelIndex WatchesDataModel::GetTopmostIndex(const QModelIndex& index)
{
if (index.parent().isValid())
{
return GetTopmostIndex(index.parent());
}
return index;
}
void WatchesDataModel::UpdateMatchingDVs(const AZ::ScriptContextDebug::DebugValue& newData)
{
bool startedReset = false;
for (int idx = 0; idx < m_DebugValues.size(); ++idx)
{
if (m_DebugValues[idx].m_name == newData.m_name)
{
if (!startedReset)
{
beginResetModel();
}
startedReset = true;
ApplyNewData(m_DebugValues[idx], newData);
}
}
if (startedReset)
{
endResetModel();
}
}
void WatchesDataModel::ApplyNewData(AZ::ScriptContextDebug::DebugValue& original, const AZ::ScriptContextDebug::DebugValue& newvalues)
{
//AZ_TracePrintf("LUA Editor", "ApplyNewData: name=%s to name=%s\n", original.m_name.c_str(),newvalues.m_name.c_str());
// deep copy operator=, original is the locally maintained version and newvalues just arrived from outside
original = newvalues;
m_parentsDirty = true;
//DVRecursePrint( original, 0 );
}
void WatchesDataModel::DVRecursePrint(const AZ::ScriptContextDebug::DebugValue& dv, int indent) const
{
for (int idx = 0; idx < dv.m_elements.size(); ++idx)
{
AZ_TracePrintf("LUA Editor", "(%d) of (%d) - %s := %s\n", idx, dv.m_elements.size(), dv.m_name.c_str(), dv.m_value.c_str());
DVRecursePrint(dv.m_elements[idx], indent + 1);
}
}
void WatchesDataModel::RegenerateParentsMap() const
{
// lazy recalculation of the map from DV* to its parent DV*
if (m_parentsDirty)
{
m_parents.clear();
for (int idx = 0; idx < m_DebugValues.size(); ++idx)
{
m_parents[ &m_DebugValues[idx] ] = NULL;
RegenerateParentsMapRecurse(m_DebugValues[idx]);
}
m_parentsDirty = false;
}
}
void WatchesDataModel::RegenerateParentsMapRecurse(const AZ::ScriptContextDebug::DebugValue& dv) const
{
for (int idx = 0; idx < dv.m_elements.size(); ++idx)
{
m_parents[ &dv.m_elements[idx] ] = &dv;
RegenerateParentsMapRecurse(dv.m_elements[idx]);
}
}
int WatchesDataModel::columnCount (const QModelIndex& parent) const
{
(void)parent;
return 3; // name + value + type
}
QVariant WatchesDataModel::data (const QModelIndex& index, int role) const
{
if (role == Qt::DisplayRole)
{
if (!IsRealIndex(index))
{
if (index.column() == 0)
{
return QVariant("<new watch>");
}
return QVariant();
}
const AZ::ScriptContextDebug::DebugValue *dv = GetDV(index);
// retrieve the DebugValue for this index
// return a variant built from its displayable text name
if (dv)
{
switch (index.column())
{
case 0: // name
return QVariant(dv->m_name.c_str());
break;
case 1: // value
return QVariant(dv->m_value.c_str());
break;
case 2: // type lookup
return QVariant(SafetyType(dv->m_type));
break;
}
}
}
return QVariant();
}
Qt::ItemFlags WatchesDataModel::flags (const QModelIndex& index) const
{
if (!index.isValid())
{
return Qt::ItemFlags();
}
const AZ::ScriptContextDebug::DebugValue *dv = GetDV(index);
switch (index.column())
{
case 0: // NAME
{
// LUA locals do not allow the name to change, only the value
bool RO = (m_OperatingMode == WATCHES_MODE_LOCALS);
if (index.isValid() && !index.parent().isValid() && !RO)
{
// topmost
//if(dv) AZ_TracePrintf("LUA Editor", "EDITABLE flags RO( %d ) dv( %s )\n", (int)RO, dv->m_name );
return Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}
else
{
// children names cannot be edited
//if(dv) AZ_TracePrintf("LUA Editor", "LOCKED flags RO( %d ) dv( %s )\n", (int)RO, dv->m_name );
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}
}
break;
case 1: // VALUE
{
bool RO =
(dv == NULL)
|| (dv->m_type == LUA_TFUNCTION)
|| (dv->m_type == LUA_TNONE)
|| (dv->m_flags & AZ::ScriptContextDebug::DebugValue::FLAG_READ_ONLY);
if (IsRealIndex(index) && !RO)
{
//if(dv) AZ_TracePrintf("LUA Editor", "EDITABLE flags RO( %d ) dv( %s )\n", (int)RO, dv->m_name );
return Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}
else
{
//if(dv) AZ_TracePrintf("LUA Editor", "LOCKED flags RO( %d ) dv( %s )\n", (int)RO, dv->m_name );
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}
}
break;
case 2: // TYPE LOOKUP
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
break;
}
return Qt::ItemIsEnabled;
}
QModelIndex WatchesDataModel::index (int row, int column, const QModelIndex& index) const
{
RegenerateParentsMap();
//Given a model index for a parent item, this function allows views and delegates to access children of that item.
//If no valid child item - corresponding to the specified row, column, and parent model index, can be found,
//the function must return QModelIndex(), which is an invalid model index.
if ((row < 0) || (column < 0))
{
return QModelIndex();
}
if (column > 2)
{
return QModelIndex();
}
if (!index.isValid()) // its a root element.
{
if (row < m_DebugValues.size())
{
return createIndex(row, column, (void*)&m_DebugValues[row]);
}
else
{
if ((m_OperatingMode == WATCHES_MODE_GENERAL) && (row == m_DebugValues.size()))
{
return createIndex(row, column, (void*)NULL);
}
return QModelIndex();
}
}
// internal pointer is the DebugValue address
if (IsRealIndex(index))
{
const AZ::ScriptContextDebug::DebugValue *dv = GetDV(index);
if (row < dv->m_elements.size())
{
return createIndex(row, column, (void*)&dv->m_elements[row]);
}
}
return QModelIndex();
}
QModelIndex WatchesDataModel::parent (const QModelIndex& index) const
{
RegenerateParentsMap();
if (!index.isValid())
{
return QModelIndex();
}
if (index.internalPointer() != NULL)
{
const AZ::ScriptContextDebug::DebugValue *dv = static_cast<const AZ::ScriptContextDebug::DebugValue *>(index.internalPointer());
ParentContainer::const_iterator found = m_parents.find(dv);
AZ_Assert(found != m_parents.end(), "Invalid node.");
const AZ::ScriptContextDebug::DebugValue *parentDV = found->second;
if (parentDV)
{
// find which index that parent is, in its own parent.
ParentContainer::const_iterator foundParentParent = m_parents.find(parentDV);
AZ_Assert(found != m_parents.end(), "Invalid node.");
const AZ::ScriptContextDebug::DebugValue *parentParentDV = foundParentParent->second;
if (!parentParentDV)
{
// a root element.
for (int idx = 0; idx < m_DebugValues.size(); ++idx)
{
if (&m_DebugValues[idx] == parentDV)
{
return createIndex(idx, 0, (void*)parentDV);
}
}
}
else
{
// it actually has a parent.
for (int idx = 0; idx < parentParentDV->m_elements.size(); ++idx)
{
if (&parentParentDV->m_elements[idx] == parentDV)
{
return createIndex(idx, 0, (void*)parentDV);
}
}
}
}
}
return QModelIndex();
}
int WatchesDataModel::rowCount (const QModelIndex& index) const
{
RegenerateParentsMap();
size_t count = 0;
if (!index.isValid())
{
if (m_OperatingMode == WATCHES_MODE_LOCALS)
{
//AZ_TracePrintf("LUA Editor", "rowCount( LOCALS )\n");
count = m_DebugValues.size();
}
else
{
//AZ_TracePrintf("LUA Editor", "rowCount( GENERAL )\n");
count = m_DebugValues.size() + 1; // +1 offset for the <new watch> line which is only at the topmost
}
// invalid parent is the dummy index in QT holding the topmost rows
}
else if (IsRealIndex(index))
{
if (index.column() == 0)
{
//AZ_TracePrintf("LUA Editor", "rowCount( ISREAL )\n");
const AZ::ScriptContextDebug::DebugValue *dv = static_cast<const AZ::ScriptContextDebug::DebugValue *>(index.internalPointer());
count = dv->m_elements.size();
}
}
return (int)count;
}
QVariant WatchesDataModel::headerData (int section, Qt::Orientation orientation, int role) const
{
(void)orientation;
if (role == Qt::DisplayRole)
{
switch (section)
{
case 0: // name
return QVariant(tr("Name"));
break;
case 1: // value
return QVariant(tr("Value"));
break;
case 2: // type lookup
return QVariant(tr("LUA Type"));
break;
}
return QVariant();
}
return QVariant();
}
bool WatchesDataModel::setData (const QModelIndex& index, const QVariant& value, int role)
{
//AZ_TracePrintf("LUA Editor", "SetData: val=%s\n", value.toString().toAscii());
// false default means that we didn't allow this change for some reason
bool result = false;
if (role == Qt::EditRole)
{
switch (index.column())
{
case 0:
if (!index.parent().isValid())
{
// column 0 := NAME. we changed this watch to look for something completely different
// THIS IS ONLY POSSIBLE IF WE ARE A TOPMOST PARENT, names of children should not be changed
// true means this new data was accepted
if (index.row() < m_DebugValues.size())
{
// this is a real row
AZ::ScriptContextDebug::DebugValue *dv = static_cast<AZ::ScriptContextDebug::DebugValue *>(index.internalPointer());
if (dv)
{
AZStd::string str = value.toString().toUtf8().data();
dv->m_name = str.c_str();
emit dataChanged(index, index);
result = true;
}
}
else if (m_OperatingMode == WATCHES_MODE_GENERAL)
{
// this is the phony <new watch> row
// display role string for this is synthesized, not stored
AZStd::string str = value.toString().toUtf8().data();
if (str.length())
{
beginResetModel();
AddWatch(str);
result = true;
endResetModel();
}
}
}
else
{
AZ::ScriptContextDebug::DebugValue *dv = static_cast<AZ::ScriptContextDebug::DebugValue *>(index.internalPointer());
if (dv)
{
AZStd::string str = value.toString().toUtf8().data();
dv->m_name = str.c_str();
emit dataChanged(index, index);
result = true;
}
}
break;
case 1:
// column 1 := VALUE. we want to message the outside world of the change
// true means this new data was accepted
result = true;
AZ::ScriptContextDebug::DebugValue *dv = static_cast<AZ::ScriptContextDebug::DebugValue *>(index.internalPointer());
if (dv)
{
AZStd::string str = value.toString().toUtf8().data();
dv->m_value = str.c_str();
const QModelIndex qmi = GetTopmostIndex(index);
const AZ::ScriptContextDebug::DebugValue *cdv = GetDV(qmi);
EBUS_EVENT(LUAEditor::LUAEditorDebuggerMessages::Bus, SetValue, *cdv);
emit dataChanged(index, index);
}
break;
}
}
return result;
}
const char* WatchesDataModel::SafetyType(char c) const
{
if (c <= LUA_TNONE || c > LUA_NUMTAGS)
{
return "<invalid>";
}
return WatchesPanel::typeStringLUT[static_cast<int>(c)];
}
const bool WatchesDataModel::IsRealIndex(const QModelIndex& index) const
{
if ((!index.isValid() && index.row() >= m_DebugValues.size()) || index.internalPointer() == NULL)
{
return false;
}
return true;
}
bool WatchesDataModel::IsTypeChangeAllowed(const QModelIndex& index) const
{
const AZ::ScriptContextDebug::DebugValue *dv = GetDV(index);
if (dv)
{
return 0 != (dv->m_flags & AZ::ScriptContextDebug::DebugValue::FLAG_ALLOW_TYPE_CHANGE);
}
return false;
}
void WatchesDataModel::SetType(const QModelIndex& index, char newType)
{
AZ::ScriptContextDebug::DebugValue *dv = static_cast<AZ::ScriptContextDebug::DebugValue *>(index.internalPointer());
if (dv)
{
dv->m_type = newType;
const QModelIndex qmi = GetTopmostIndex(index);
const AZ::ScriptContextDebug::DebugValue *cdv = GetDV(qmi);
EBUS_EVENT(LUAEditor::LUAEditorDebuggerMessages::Bus, SetValue, *cdv);
emit dataChanged(index, index);
}
}