Moves Code/Tools/Standalone to Code/Tools/LuaIDE
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,874 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user