WatchesPanel.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "WatchesPanel.hxx"
  9. #include "AzCore/Debug/Trace.h"
  10. #include "LUAEditorDebuggerMessages.h"
  11. #include <AzCore/Casting/numeric_cast.h>
  12. #include <AzCore/Script/lua/lua.h>
  13. #include <Source/LUA/moc_WatchesPanel.cpp>
  14. #include <QSortFilterProxyModel>
  15. #include <QMenu>
  16. #include <QAction>
  17. #include <QKeyEvent>
  18. namespace WatchesPanel
  19. {
  20. static constexpr AZStd::array typeStringLUT {
  21. "NIL", // LUA_TNIL
  22. "BOOLEAN", // LUA_TBOOLEAN
  23. "LIGHTUSERDATA", // LUA_TLIGHTUSERDATA
  24. "NUMBER", // LUA_TNUMBER
  25. "STRING", // LUA_TSTRING
  26. "TABLE", // LUA_TTABLE
  27. "FUNCTION", // LUA_TFUNCTION
  28. "USERDATA", // LUA_TUSERDATA
  29. "THREAD", // LUA_TTHREAD
  30. };
  31. }
  32. class WatchesFilterModel
  33. : public QSortFilterProxyModel
  34. {
  35. public:
  36. AZ_CLASS_ALLOCATOR(WatchesFilterModel, AZ::SystemAllocator);
  37. WatchesFilterModel(QObject* pParent)
  38. : QSortFilterProxyModel(pParent)
  39. {
  40. setFilterCaseSensitivity(Qt::CaseSensitive);
  41. setDynamicSortFilter(false);
  42. }
  43. protected:
  44. virtual bool lessThan(const QModelIndex& left, const QModelIndex& right) const
  45. {
  46. return QSortFilterProxyModel::lessThan(left, right);
  47. }
  48. };
  49. DHWatchesWidget::DHWatchesWidget(QWidget* parent)
  50. : AzToolsFramework::QTreeViewWithStateSaving(parent)
  51. {
  52. setFocusPolicy(Qt::StrongFocus); // required for key press handling
  53. setEnabled(false);
  54. setSortingEnabled(true);
  55. sortByColumn(0, Qt::AscendingOrder);
  56. SetOperatingMode(WATCHES_MODE_GENERAL);
  57. m_OperatingState = WATCHES_STATE_DISCONNECTED;
  58. m_pFilterModel = aznew WatchesFilterModel(this);
  59. m_pFilterModel->setSourceModel(&m_DM);
  60. setModel(m_pFilterModel);
  61. LUAEditor::LUAWatchesDebuggerMessages::Handler::BusConnect();
  62. LUAEditor::LUALocalsTrackerMessages::Handler::BusConnect();
  63. LUAEditor::LUABreakpointTrackerMessages::Handler::BusConnect();
  64. connectDataModelUpdate();
  65. connect(this, &DHWatchesWidget::doubleClicked, this, &DHWatchesWidget::OnDoubleClicked);
  66. auto crc = AZ::Crc32("StandaloneToolsWatchesPanel");
  67. InitializeTreeViewSaving(crc);
  68. ForceSelectNewWatch();
  69. }
  70. void DHWatchesWidget::disconnectDataModelUpdate()
  71. {
  72. disconnect(m_dataModelDataChangedConnection);
  73. disconnect(m_dataModelRestConnection);
  74. }
  75. void DHWatchesWidget::connectDataModelUpdate()
  76. {
  77. m_dataModelDataChangedConnection = connect(&m_DM, &WatchesDataModel::dataChanged, this, &DHWatchesWidget::OnItemChanged);
  78. m_dataModelRestConnection = connect(&m_DM, &WatchesDataModel::modelReset, this, &DHWatchesWidget::OnItemChanged);
  79. }
  80. DHWatchesWidget::~DHWatchesWidget()
  81. {
  82. LUAEditor::LUABreakpointTrackerMessages::Handler::BusDisconnect();
  83. LUAEditor::LUALocalsTrackerMessages::Handler::BusDisconnect();
  84. LUAEditor::LUAWatchesDebuggerMessages::Handler::BusDisconnect();
  85. }
  86. void DHWatchesWidget::SetOperatingMode(WatchesOperatingMode newMode)
  87. {
  88. m_OperatingMode = newMode;
  89. m_DM.SetOperatingMode(newMode);
  90. }
  91. void DHWatchesWidget::OnDebuggerAttached()
  92. {
  93. m_OperatingState = WATCHES_STATE_CONNECTED;
  94. setEnabled(true);
  95. if (m_OperatingMode == WATCHES_MODE_GENERAL)
  96. {
  97. CaptureVariables();
  98. }
  99. }
  100. void DHWatchesWidget::OnDebuggerDetached()
  101. {
  102. m_OperatingState = WATCHES_STATE_DISCONNECTED;
  103. LocalsClear();
  104. setEnabled(false);
  105. }
  106. void DHWatchesWidget::CaptureVariables()
  107. {
  108. //AZ_TracePrintf("LUA Debug", "CaptureVariables:\n");
  109. if (m_OperatingState == WATCHES_STATE_CONNECTED)
  110. {
  111. // query watched variables
  112. int shortLoop = m_OperatingMode == WATCHES_MODE_GENERAL ? 1 : 0;
  113. int rowCount = m_DM.rowCount() - shortLoop; // negative offset to skip the <new watch> for the not-locals panel
  114. //AZ_TracePrintf("LUA Debug", "CaptureVariables( count = %d)\n",rowCount);
  115. for (int i = 0; i < rowCount; ++i)
  116. {
  117. QModelIndex indexChild = m_DM.index(i, 0);
  118. if (indexChild.isValid())
  119. {
  120. QString name;
  121. name = m_DM.data(indexChild).toString();
  122. //AZ_TracePrintf("LUA Editor", " - RequestWatchedVariable( %s )\n", AZStd::string(name.toAscii()));
  123. LUAEditor::LUAWatchesRequestMessagesRequestBus::Broadcast(&LUAEditor::LUAWatchesRequestMessages::RequestWatchedVariable, AZStd::string(name.toUtf8().data()));
  124. // results will return via WatchesUpdate() values asynchronously
  125. // NB: not recursive, only top-level variable names are requested
  126. }
  127. }
  128. }
  129. }
  130. void DHWatchesWidget::BreakpointHit(const LUAEditor::Breakpoint& bp)
  131. {
  132. (void)bp;
  133. if (m_OperatingMode == WATCHES_MODE_GENERAL)
  134. {
  135. CaptureVariables();
  136. }
  137. else // m_OperatingMode == WATCHES_MODE_LOCALS
  138. {
  139. if (isVisible())
  140. {
  141. LUAEditor::LUAEditorDebuggerMessagesRequestBus::Broadcast(&LUAEditor::LUAEditorDebuggerMessages::EnumLocals);
  142. }
  143. }
  144. }
  145. void DHWatchesWidget::WatchesUpdate(const AZ::ScriptContextDebug::DebugValue& topmostDebugReference)
  146. {
  147. //AZ_TracePrintf("LUA Editor", "incoming WatchesUpdate( %s )\n", topmostDebugReference.m_name);
  148. disconnectDataModelUpdate();
  149. m_DM.UpdateMatchingDVs(topmostDebugReference);
  150. connectDataModelUpdate();
  151. ApplyTreeViewSnapshot();
  152. }
  153. void DHWatchesWidget::OnItemChanged()
  154. {
  155. if (m_OperatingMode == WATCHES_MODE_GENERAL)
  156. {
  157. CaptureVariables();
  158. }
  159. }
  160. void DHWatchesWidget::OnDoubleClicked(const QModelIndex& index)
  161. {
  162. //AZ_TracePrintf("BP", "OnDoubleClicked() %d, %d\n", index.row(), index.column());
  163. if (m_OperatingState == WATCHES_STATE_CONNECTED)
  164. {
  165. if (m_pFilterModel->mapToSource(index).column() == 2)
  166. {
  167. // double click popup only works on the TYPE column
  168. {
  169. if (m_DM.IsTypeChangeAllowed(m_pFilterModel->mapToSource(index)))
  170. {
  171. QMenu* popupMenu = new QMenu(this);
  172. {
  173. QMenu* layoutMenu = new QMenu(tr("LUA Value Type"), this);
  174. layoutMenu->addAction(tr("Boolean"));
  175. layoutMenu->addAction(tr("Number"));
  176. layoutMenu->addAction(tr("String"));
  177. popupMenu->addMenu(layoutMenu);
  178. QAction* act = popupMenu->exec(QCursor::pos());
  179. if (act)
  180. {
  181. char newType = LUA_TNONE;
  182. if (act->text() == tr("Boolean"))
  183. {
  184. newType = LUA_TBOOLEAN;
  185. }
  186. if (act->text() == tr("Number"))
  187. {
  188. newType = LUA_TNUMBER;
  189. }
  190. if (act->text() == tr("String"))
  191. {
  192. newType = LUA_TSTRING;
  193. }
  194. m_DM.SetType(m_pFilterModel->mapToSource(index), newType);
  195. }
  196. }
  197. }
  198. }
  199. }
  200. }
  201. }
  202. void DHWatchesWidget::LocalsUpdate(const AZStd::vector<AZStd::string>& vars)
  203. {
  204. disconnectDataModelUpdate();
  205. if (m_OperatingMode == WATCHES_MODE_LOCALS)
  206. {
  207. LocalsClear();
  208. //AZ_TracePrintf("LUA Editor", "LOCALSUPDATE\n");
  209. for (AZStd::vector<AZStd::string>::const_iterator it = vars.begin(); it != vars.end(); ++it)
  210. {
  211. //AZ_TracePrintf("LUA Editor", " - LocalsUpdate( %s )\n", it->c_str());
  212. m_DM.AddWatch(it->c_str());
  213. }
  214. }
  215. connectDataModelUpdate();
  216. }
  217. void DHWatchesWidget::LocalsClear()
  218. {
  219. //AZ_TracePrintf("LUA Editor", "LOCALS Clear\n");
  220. disconnectDataModelUpdate();
  221. if (m_OperatingMode == WATCHES_MODE_LOCALS)
  222. {
  223. if (m_DM.rowCount())
  224. {
  225. // local implementation of removeRows handles the begin/end cycle
  226. m_DM.removeRows(0, m_DM.rowCount());
  227. }
  228. }
  229. connectDataModelUpdate();
  230. }
  231. void DHWatchesWidget::keyPressEvent(QKeyEvent* event)
  232. {
  233. if (m_OperatingMode == WATCHES_MODE_GENERAL)
  234. {
  235. if (event->isAccepted())
  236. {
  237. if (event->key() == Qt::Key_Delete)
  238. {
  239. QModelIndexList mil = selectedIndexes();
  240. for (QModelIndexList::iterator iter = mil.begin(); iter != mil.end(); ++iter)
  241. {
  242. QModelIndex toRemove = m_pFilterModel->mapToSource(*iter);
  243. m_DM.RemoveWatch(toRemove);
  244. }
  245. event->accept();
  246. return;
  247. }
  248. if ((event->key() == Qt::Key_Enter) || (event->key() == Qt::Key_Return))
  249. {
  250. QModelIndexList mil = selectedIndexes();
  251. if (mil.size())
  252. {
  253. // edit the selected item by spoofing QT's "edit key"
  254. QKeyEvent evt(event->type(), Qt::Key_F2, event->modifiers());
  255. QTreeView::keyPressEvent(&evt);
  256. event->accept();
  257. return;
  258. }
  259. else
  260. {
  261. // if nothing is selected then force edit to the <new watch> item
  262. ForceSelectNewWatch();
  263. event->ignore();
  264. QTreeView::keyPressEvent(event);
  265. return;
  266. }
  267. }
  268. }
  269. }
  270. event->ignore();
  271. QTreeView::keyPressEvent(event);
  272. }
  273. void DHWatchesWidget::ForceSelectNewWatch()
  274. {
  275. if (m_OperatingMode == WATCHES_MODE_GENERAL)
  276. {
  277. int row = m_DM.rowCount() - 1;
  278. QModelIndex index = m_DM.index(row, 0);
  279. selectionModel()->select(m_pFilterModel->mapFromSource(index), QItemSelectionModel::ClearAndSelect);
  280. setCurrentIndex(m_pFilterModel->mapFromSource(index));
  281. }
  282. }
  283. //------------------------------------------------------------------------
  284. WatchesDataModel::WatchesDataModel()
  285. {
  286. m_parentsDirty = false;
  287. m_OperatingMode = WATCHES_MODE_GENERAL;
  288. }
  289. WatchesDataModel::~WatchesDataModel()
  290. {
  291. }
  292. void WatchesDataModel::SetOperatingMode(WatchesOperatingMode newMode)
  293. {
  294. beginResetModel(); // this has to be a reset because changing the operating mode can change row count.
  295. m_OperatingMode = newMode;
  296. endResetModel();
  297. }
  298. void WatchesDataModel::AddWatch(const AZ::ScriptContextDebug::DebugValue& newData)
  299. {
  300. //beginResetModel(); // this has to be a reset because push_back can reallocate.
  301. beginInsertRows(QModelIndex(), (int)m_DebugValues.size(), (int)m_DebugValues.size());
  302. //AZ_TracePrintf("LUA Editor", "AddWatch( lr = %d rc = %d dvsize = %d )\n",m_OperatingMode,rowCount(),m_DebugValues.size());
  303. m_DebugValues.push_back(newData);
  304. m_parentsDirty = true;
  305. endInsertRows();
  306. }
  307. void WatchesDataModel::AddWatch(AZStd::string newName)
  308. {
  309. //AZ_TracePrintf("LUA Editor", "AddWatch( lr = %d - %s )\n",m_OperatingMode,newName.c_str());
  310. AZ::ScriptContextDebug::DebugValue dv;
  311. dv.m_name = newName;
  312. dv.m_value = "<invalid>";
  313. dv.m_type = LUA_TNONE;
  314. dv.m_typeId = AZ::ScriptTypeId{};
  315. dv.m_flags = 0;
  316. AddWatch(dv);
  317. }
  318. void WatchesDataModel::RemoveWatch(QModelIndex& index)
  319. {
  320. if (IsRealIndex(index))
  321. {
  322. const QModelIndex qmi = GetTopmostIndex(index);
  323. // we only delete full rows, incoming selections can hold many columns in that row
  324. // and so we must skip any but the first column
  325. if (qmi.isValid() && (qmi.column() == 0))
  326. {
  327. removeRow(qmi.row(), parent(qmi));
  328. }
  329. }
  330. }
  331. bool WatchesDataModel::removeRows (int row, int count, const QModelIndex& parent)
  332. {
  333. (void)count;
  334. beginRemoveRows(parent, row, row + count - 1);
  335. m_DebugValues.erase(m_DebugValues.begin() + row, m_DebugValues.begin() + row + count);
  336. m_parentsDirty = true;
  337. endRemoveRows();
  338. return true;
  339. }
  340. const AZ::ScriptContextDebug::DebugValue *WatchesDataModel::GetDV(const QModelIndex & index) const
  341. {
  342. if (IsRealIndex(index))
  343. {
  344. const AZ::ScriptContextDebug::DebugValue *dv = static_cast<const AZ::ScriptContextDebug::DebugValue *>(index.internalPointer());
  345. return dv;
  346. }
  347. return NULL;
  348. }
  349. const QModelIndex WatchesDataModel::GetTopmostIndex(const QModelIndex& index)
  350. {
  351. if (index.parent().isValid())
  352. {
  353. return GetTopmostIndex(index.parent());
  354. }
  355. return index;
  356. }
  357. void WatchesDataModel::UpdateMatchingDVs(const AZ::ScriptContextDebug::DebugValue& newData)
  358. {
  359. bool startedReset = false;
  360. for (int idx = 0; idx < m_DebugValues.size(); ++idx)
  361. {
  362. if (m_DebugValues[idx].m_name == newData.m_name)
  363. {
  364. if (!startedReset)
  365. {
  366. beginResetModel();
  367. }
  368. startedReset = true;
  369. ApplyNewData(m_DebugValues[idx], newData);
  370. }
  371. }
  372. if (startedReset)
  373. {
  374. endResetModel();
  375. }
  376. }
  377. void WatchesDataModel::ApplyNewData(AZ::ScriptContextDebug::DebugValue& original, const AZ::ScriptContextDebug::DebugValue& newvalues)
  378. {
  379. //AZ_TracePrintf("LUA Editor", "ApplyNewData: name=%s to name=%s\n", original.m_name.c_str(),newvalues.m_name.c_str());
  380. // deep copy operator=, original is the locally maintained version and newvalues just arrived from outside
  381. original = newvalues;
  382. m_parentsDirty = true;
  383. //DVRecursePrint( original, 0 );
  384. }
  385. void WatchesDataModel::DVRecursePrint(const AZ::ScriptContextDebug::DebugValue& dv, int indent) const
  386. {
  387. for (int idx = 0; idx < dv.m_elements.size(); ++idx)
  388. {
  389. 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());
  390. DVRecursePrint(dv.m_elements[idx], indent + 1);
  391. }
  392. }
  393. void WatchesDataModel::RegenerateParentsMap() const
  394. {
  395. // lazy recalculation of the map from DV* to its parent DV*
  396. if (m_parentsDirty)
  397. {
  398. m_parents.clear();
  399. for (int idx = 0; idx < m_DebugValues.size(); ++idx)
  400. {
  401. m_parents[ &m_DebugValues[idx] ] = NULL;
  402. RegenerateParentsMapRecurse(m_DebugValues[idx]);
  403. }
  404. m_parentsDirty = false;
  405. }
  406. }
  407. void WatchesDataModel::RegenerateParentsMapRecurse(const AZ::ScriptContextDebug::DebugValue& dv) const
  408. {
  409. for (int idx = 0; idx < dv.m_elements.size(); ++idx)
  410. {
  411. m_parents[ &dv.m_elements[idx] ] = &dv;
  412. RegenerateParentsMapRecurse(dv.m_elements[idx]);
  413. }
  414. }
  415. int WatchesDataModel::columnCount (const QModelIndex& parent) const
  416. {
  417. (void)parent;
  418. return 3; // name + value + type
  419. }
  420. QVariant WatchesDataModel::data (const QModelIndex& index, int role) const
  421. {
  422. if (role == Qt::DisplayRole)
  423. {
  424. if (!IsRealIndex(index))
  425. {
  426. if (index.column() == 0)
  427. {
  428. return QVariant("<new watch>");
  429. }
  430. return QVariant();
  431. }
  432. const AZ::ScriptContextDebug::DebugValue *dv = GetDV(index);
  433. // retrieve the DebugValue for this index
  434. // return a variant built from its displayable text name
  435. if (dv)
  436. {
  437. switch (index.column())
  438. {
  439. case 0: // name
  440. return QVariant(dv->m_name.c_str());
  441. break;
  442. case 1: // value
  443. return QVariant(dv->m_value.c_str());
  444. break;
  445. case 2: // type lookup
  446. return QVariant(SafetyType(dv->m_type));
  447. break;
  448. }
  449. }
  450. }
  451. return QVariant();
  452. }
  453. Qt::ItemFlags WatchesDataModel::flags (const QModelIndex& index) const
  454. {
  455. if (!index.isValid())
  456. {
  457. return Qt::ItemFlags();
  458. }
  459. const AZ::ScriptContextDebug::DebugValue *dv = GetDV(index);
  460. switch (index.column())
  461. {
  462. case 0: // NAME
  463. {
  464. // LUA locals do not allow the name to change, only the value
  465. bool RO = (m_OperatingMode == WATCHES_MODE_LOCALS);
  466. if (index.isValid() && !index.parent().isValid() && !RO)
  467. {
  468. // topmost
  469. //if(dv) AZ_TracePrintf("LUA Editor", "EDITABLE flags RO( %d ) dv( %s )\n", (int)RO, dv->m_name );
  470. return Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled;
  471. }
  472. else
  473. {
  474. // children names cannot be edited
  475. //if(dv) AZ_TracePrintf("LUA Editor", "LOCKED flags RO( %d ) dv( %s )\n", (int)RO, dv->m_name );
  476. return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
  477. }
  478. }
  479. break;
  480. case 1: // VALUE
  481. {
  482. bool RO =
  483. (dv == NULL)
  484. || (dv->m_type == LUA_TFUNCTION)
  485. || (static_cast<int>(dv->m_type) == LUA_TNONE)
  486. || (dv->m_flags & AZ::ScriptContextDebug::DebugValue::FLAG_READ_ONLY);
  487. if (IsRealIndex(index) && !RO)
  488. {
  489. //if(dv) AZ_TracePrintf("LUA Editor", "EDITABLE flags RO( %d ) dv( %s )\n", (int)RO, dv->m_name );
  490. return Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled;
  491. }
  492. else
  493. {
  494. //if(dv) AZ_TracePrintf("LUA Editor", "LOCKED flags RO( %d ) dv( %s )\n", (int)RO, dv->m_name );
  495. return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
  496. }
  497. }
  498. break;
  499. case 2: // TYPE LOOKUP
  500. return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
  501. break;
  502. }
  503. return Qt::ItemIsEnabled;
  504. }
  505. QModelIndex WatchesDataModel::index (int row, int column, const QModelIndex& index) const
  506. {
  507. RegenerateParentsMap();
  508. //Given a model index for a parent item, this function allows views and delegates to access children of that item.
  509. //If no valid child item - corresponding to the specified row, column, and parent model index, can be found,
  510. //the function must return QModelIndex(), which is an invalid model index.
  511. if ((row < 0) || (column < 0))
  512. {
  513. return QModelIndex();
  514. }
  515. if (column > 2)
  516. {
  517. return QModelIndex();
  518. }
  519. if (!index.isValid()) // its a root element.
  520. {
  521. if (row < m_DebugValues.size())
  522. {
  523. return createIndex(row, column, (void*)&m_DebugValues[row]);
  524. }
  525. else
  526. {
  527. if ((m_OperatingMode == WATCHES_MODE_GENERAL) && (row == m_DebugValues.size()))
  528. {
  529. return createIndex(row, column, (void*)NULL);
  530. }
  531. return QModelIndex();
  532. }
  533. }
  534. // internal pointer is the DebugValue address
  535. if (IsRealIndex(index))
  536. {
  537. const AZ::ScriptContextDebug::DebugValue *dv = GetDV(index);
  538. if (row < dv->m_elements.size())
  539. {
  540. return createIndex(row, column, (void*)&dv->m_elements[row]);
  541. }
  542. }
  543. return QModelIndex();
  544. }
  545. QModelIndex WatchesDataModel::parent (const QModelIndex& index) const
  546. {
  547. RegenerateParentsMap();
  548. if (!index.isValid())
  549. {
  550. return QModelIndex();
  551. }
  552. if (index.internalPointer() != NULL)
  553. {
  554. const AZ::ScriptContextDebug::DebugValue *dv = static_cast<const AZ::ScriptContextDebug::DebugValue *>(index.internalPointer());
  555. ParentContainer::const_iterator found = m_parents.find(dv);
  556. AZ_Assert(found != m_parents.end(), "Invalid node.");
  557. const AZ::ScriptContextDebug::DebugValue *parentDV = found->second;
  558. if (parentDV)
  559. {
  560. // find which index that parent is, in its own parent.
  561. ParentContainer::const_iterator foundParentParent = m_parents.find(parentDV);
  562. AZ_Assert(found != m_parents.end(), "Invalid node.");
  563. const AZ::ScriptContextDebug::DebugValue *parentParentDV = foundParentParent->second;
  564. if (!parentParentDV)
  565. {
  566. // a root element.
  567. for (int idx = 0; idx < m_DebugValues.size(); ++idx)
  568. {
  569. if (&m_DebugValues[idx] == parentDV)
  570. {
  571. return createIndex(idx, 0, (void*)parentDV);
  572. }
  573. }
  574. }
  575. else
  576. {
  577. // it actually has a parent.
  578. for (int idx = 0; idx < parentParentDV->m_elements.size(); ++idx)
  579. {
  580. if (&parentParentDV->m_elements[idx] == parentDV)
  581. {
  582. return createIndex(idx, 0, (void*)parentDV);
  583. }
  584. }
  585. }
  586. }
  587. }
  588. return QModelIndex();
  589. }
  590. int WatchesDataModel::rowCount (const QModelIndex& index) const
  591. {
  592. RegenerateParentsMap();
  593. size_t count = 0;
  594. if (!index.isValid())
  595. {
  596. if (m_OperatingMode == WATCHES_MODE_LOCALS)
  597. {
  598. //AZ_TracePrintf("LUA Editor", "rowCount( LOCALS )\n");
  599. count = m_DebugValues.size();
  600. }
  601. else
  602. {
  603. //AZ_TracePrintf("LUA Editor", "rowCount( GENERAL )\n");
  604. count = m_DebugValues.size() + 1; // +1 offset for the <new watch> line which is only at the topmost
  605. }
  606. // invalid parent is the dummy index in QT holding the topmost rows
  607. }
  608. else if (IsRealIndex(index))
  609. {
  610. if (index.column() == 0)
  611. {
  612. //AZ_TracePrintf("LUA Editor", "rowCount( ISREAL )\n");
  613. const AZ::ScriptContextDebug::DebugValue *dv = static_cast<const AZ::ScriptContextDebug::DebugValue *>(index.internalPointer());
  614. count = dv->m_elements.size();
  615. }
  616. }
  617. return (int)count;
  618. }
  619. QVariant WatchesDataModel::headerData (int section, Qt::Orientation orientation, int role) const
  620. {
  621. (void)orientation;
  622. if (role == Qt::DisplayRole)
  623. {
  624. switch (section)
  625. {
  626. case 0: // name
  627. return QVariant(tr("Name"));
  628. break;
  629. case 1: // value
  630. return QVariant(tr("Value"));
  631. break;
  632. case 2: // type lookup
  633. return QVariant(tr("LUA Type"));
  634. break;
  635. }
  636. return QVariant();
  637. }
  638. return QVariant();
  639. }
  640. bool WatchesDataModel::setData (const QModelIndex& index, const QVariant& value, int role)
  641. {
  642. //AZ_TracePrintf("LUA Editor", "SetData: val=%s\n", value.toString().toAscii());
  643. // false default means that we didn't allow this change for some reason
  644. bool result = false;
  645. if (role == Qt::EditRole)
  646. {
  647. switch (index.column())
  648. {
  649. case 0:
  650. if (!index.parent().isValid())
  651. {
  652. // column 0 := NAME. we changed this watch to look for something completely different
  653. // THIS IS ONLY POSSIBLE IF WE ARE A TOPMOST PARENT, names of children should not be changed
  654. // true means this new data was accepted
  655. if (index.row() < m_DebugValues.size())
  656. {
  657. // this is a real row
  658. AZ::ScriptContextDebug::DebugValue *dv = static_cast<AZ::ScriptContextDebug::DebugValue *>(index.internalPointer());
  659. if (dv)
  660. {
  661. AZStd::string str = value.toString().toUtf8().data();
  662. dv->m_name = str.c_str();
  663. emit dataChanged(index, index);
  664. result = true;
  665. }
  666. }
  667. else if (m_OperatingMode == WATCHES_MODE_GENERAL)
  668. {
  669. // this is the phony <new watch> row
  670. // display role string for this is synthesized, not stored
  671. AZStd::string str = value.toString().toUtf8().data();
  672. if (str.length())
  673. {
  674. beginResetModel();
  675. AddWatch(str);
  676. result = true;
  677. endResetModel();
  678. }
  679. }
  680. }
  681. else
  682. {
  683. AZ::ScriptContextDebug::DebugValue *dv = static_cast<AZ::ScriptContextDebug::DebugValue *>(index.internalPointer());
  684. if (dv)
  685. {
  686. AZStd::string str = value.toString().toUtf8().data();
  687. dv->m_name = str.c_str();
  688. emit dataChanged(index, index);
  689. result = true;
  690. }
  691. }
  692. break;
  693. case 1:
  694. // column 1 := VALUE. we want to message the outside world of the change
  695. // true means this new data was accepted
  696. result = true;
  697. AZ::ScriptContextDebug::DebugValue *dv = static_cast<AZ::ScriptContextDebug::DebugValue *>(index.internalPointer());
  698. if (dv)
  699. {
  700. AZStd::string str = value.toString().toUtf8().data();
  701. dv->m_value = str.c_str();
  702. const QModelIndex qmi = GetTopmostIndex(index);
  703. const AZ::ScriptContextDebug::DebugValue *cdv = GetDV(qmi);
  704. LUAEditor::LUAEditorDebuggerMessagesRequestBus::Broadcast(&LUAEditor::LUAEditorDebuggerMessages::SetValue, *cdv);
  705. emit dataChanged(index, index);
  706. }
  707. break;
  708. }
  709. }
  710. return result;
  711. }
  712. const char* WatchesDataModel::SafetyType(char c) const
  713. {
  714. if (static_cast<int>(c) <= LUA_TNONE || c > LUA_NUMTAGS)
  715. {
  716. return "<invalid>";
  717. }
  718. static_assert(WatchesPanel::typeStringLUT.size() == LUA_NUMTAGS, "number of lua tags does not match the number of typeStringLUT");
  719. return WatchesPanel::typeStringLUT[aznumeric_cast<int>(c)];
  720. }
  721. const bool WatchesDataModel::IsRealIndex(const QModelIndex& index) const
  722. {
  723. if ((!index.isValid() && index.row() >= m_DebugValues.size()) || index.internalPointer() == NULL)
  724. {
  725. return false;
  726. }
  727. return true;
  728. }
  729. bool WatchesDataModel::IsTypeChangeAllowed(const QModelIndex& index) const
  730. {
  731. const AZ::ScriptContextDebug::DebugValue *dv = GetDV(index);
  732. if (dv)
  733. {
  734. return 0 != (dv->m_flags & AZ::ScriptContextDebug::DebugValue::FLAG_ALLOW_TYPE_CHANGE);
  735. }
  736. return false;
  737. }
  738. void WatchesDataModel::SetType(const QModelIndex& index, char newType)
  739. {
  740. AZ::ScriptContextDebug::DebugValue *dv = static_cast<AZ::ScriptContextDebug::DebugValue *>(index.internalPointer());
  741. if (dv)
  742. {
  743. dv->m_type = newType;
  744. const QModelIndex qmi = GetTopmostIndex(index);
  745. const AZ::ScriptContextDebug::DebugValue *cdv = GetDV(qmi);
  746. LUAEditor::LUAEditorDebuggerMessagesRequestBus::Broadcast(&LUAEditor::LUAEditorDebuggerMessages::SetValue, *cdv);
  747. emit dataChanged(index, index);
  748. }
  749. }