BreakpointPanel.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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 "BreakpointPanel.hxx"
  9. #include <AzCore/Debug/Trace.h>
  10. #include <AzFramework/StringFunc/StringFunc.h>
  11. #include <Source/LUA/moc_BreakpointPanel.cpp>
  12. #include <QAction>
  13. #include <QMenu>
  14. class NumericQTableWidgetItem : public QTableWidgetItem
  15. {
  16. public:
  17. using QTableWidgetItem::QTableWidgetItem;
  18. bool operator<(const QTableWidgetItem& other) const override
  19. {
  20. int num1 = text().toInt();
  21. int num2 = other.text().toInt();
  22. return num1 < num2;
  23. }
  24. };
  25. DHBreakpointsWidget::DHBreakpointsWidget(QWidget* parent)
  26. : QTableWidget(parent)
  27. , m_PauseUpdates(false)
  28. {
  29. connect(this, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(OnDoubleClicked(const QModelIndex&)));
  30. LUABreakpointTrackerMessages::Handler::BusConnect();
  31. setContextMenuPolicy(Qt::CustomContextMenu);
  32. QObject::connect(this, &QWidget::customContextMenuRequested, this, &DHBreakpointsWidget::CreateContextMenu);
  33. setSortingEnabled(true);
  34. }
  35. DHBreakpointsWidget::~DHBreakpointsWidget()
  36. {
  37. LUABreakpointTrackerMessages::Handler::BusDisconnect();
  38. clearContents();
  39. setRowCount(0);
  40. }
  41. void DHBreakpointsWidget::PullFromContext()
  42. {
  43. const LUAEditor::BreakpointMap* myData = NULL;
  44. LUAEditor::LUABreakpointRequestMessages::Bus::BroadcastResult(
  45. myData, &LUAEditor::LUABreakpointRequestMessages::Bus::Events::RequestBreakpoints);
  46. AZ_Assert(myData, "Nobody responded to the request breakpoints message.");
  47. BreakpointsUpdate(*myData);
  48. }
  49. void DHBreakpointsWidget::CreateContextMenu(const QPoint& pos)
  50. {
  51. QMenu contextMenu(this);
  52. QAction* actionDeleteAll = new QAction(tr("Delete All"), this);
  53. connect(actionDeleteAll, &QAction::triggered, this, &DHBreakpointsWidget::DeleteAll);
  54. actionDeleteAll->setEnabled(rowCount() > 0);
  55. QAction* actionDeleteSelected = new QAction(tr("Delete Selected"), this);
  56. connect(actionDeleteSelected, &QAction::triggered, this, &DHBreakpointsWidget::DeleteSelected);
  57. actionDeleteSelected->setEnabled(!selectedItems().isEmpty());
  58. contextMenu.addAction(actionDeleteAll);
  59. contextMenu.addAction(actionDeleteSelected);
  60. contextMenu.exec(mapToGlobal(pos));
  61. }
  62. void DHBreakpointsWidget::DeleteAll()
  63. {
  64. while (rowCount())
  65. {
  66. RemoveRow(0);
  67. }
  68. }
  69. void DHBreakpointsWidget::DeleteSelected()
  70. {
  71. m_PauseUpdates = true;
  72. QList<QTableWidgetItem*> list = selectedItems();
  73. for (int i = list.size() - 1; i >= 0; i -= 2) // magic number 2 is the column count, will be 3 if the empty first column ever gets contents
  74. {
  75. RemoveRow(list.at(i)->row());
  76. }
  77. m_PauseUpdates = false;
  78. PullFromContext();
  79. }
  80. //////////////////////////////////////////////////////////////////////////
  81. // Debugger Messages, from the LUAEditor::LUABreakpointTrackerMessages::Bus
  82. void DHBreakpointsWidget::BreakpointsUpdate(const LUAEditor::BreakpointMap& uniqueBreakpoints)
  83. {
  84. if (!m_PauseUpdates)
  85. {
  86. // not using DeleteAll() here, this is an outside message so we only need to do internal housekeeping
  87. while (rowCount())
  88. {
  89. removeRow(0);
  90. }
  91. for (LUAEditor::BreakpointMap::const_iterator it = uniqueBreakpoints.begin(); it != uniqueBreakpoints.end(); ++it)
  92. {
  93. const LUAEditor::Breakpoint& bp = it->second;
  94. // sanity check to hopefully bypass corrupted entries
  95. // in a pure world this should never trigger
  96. // if ( (bp.m_documentLine >= 0) && (bp.m_blob.length() >= 5) ) // magic number 5
  97. {
  98. CreateBreakpoint(bp.m_assetName, bp.m_documentLine);
  99. }
  100. // else
  101. //{
  102. // AZ_TracePrintf("BP", "Corrupted Breakpoint %s at line %d Was Stripped From Incoming Data\n", bp.m_blob, bp.m_documentLine);
  103. // }
  104. }
  105. }
  106. }
  107. void DHBreakpointsWidget::BreakpointHit(const LUAEditor::Breakpoint& bp)
  108. {
  109. // clear any previous hit
  110. selectionModel()->clearSelection();
  111. // scroll to and highlight this one
  112. QList<QTableWidgetItem*> list = findItems(bp.m_assetName.c_str(), Qt::MatchExactly);
  113. QString q;
  114. q.setNum(bp.m_documentLine + 1); // +1 offset to match editor numbering
  115. for (int i = list.size() - 1; i >= 0; --i)
  116. {
  117. // magic number column #0 is the line number, 1 is the script file name
  118. QTableWidgetItem* line = item(list.at(i)->row(), 0);
  119. if (line->text() == q)
  120. {
  121. QModelIndex indexInModel = indexFromItem(line);
  122. selectionModel()->select(indexInModel, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
  123. setCurrentIndex(indexInModel);
  124. break;
  125. }
  126. }
  127. }
  128. void DHBreakpointsWidget::BreakpointResume()
  129. {
  130. // no op
  131. }
  132. void DHBreakpointsWidget::CreateBreakpoint(const AZStd::string& debugName, int lineNumber)
  133. {
  134. // AZ_TracePrintf("BP", "CreateBreakpoint %s at line %d\n", debugName.c_str(), lineNumber);
  135. int newRow = rowCount();
  136. insertRow(newRow);
  137. // magic number column #0 is the line number, 1 is the script file name
  138. AZStd::string scriptName;
  139. AzFramework::StringFunc::Path::GetFileName(debugName.c_str(), scriptName);
  140. QTableWidgetItem* newItem = new QTableWidgetItem(debugName.c_str());
  141. newItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
  142. newItem->setData(Qt::UserRole, debugName.c_str());
  143. newItem->setData(Qt::DisplayRole, scriptName.c_str());
  144. setItem(newRow, 1, newItem);
  145. newItem = new NumericQTableWidgetItem(QString().setNum(lineNumber));
  146. newItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
  147. setItem(newRow, 0, newItem);
  148. sortItems(0, Qt::AscendingOrder);
  149. }
  150. void DHBreakpointsWidget::RemoveBreakpoint(const AZStd::string& debugName, int lineNumber)
  151. {
  152. // AZ_TracePrintf("BP", "RemoveBreakpoint %s at line %d\n", debugName.c_str(), lineNumber);
  153. QList<QTableWidgetItem*> list = findItems(debugName.c_str(), Qt::MatchExactly);
  154. QString q;
  155. q.setNum(lineNumber + 1); // +1 offset to match editor numbering
  156. for (int i = list.size() - 1; i >= 0; --i)
  157. {
  158. // magic number column #0 is the line number, 1 is the script file name
  159. QTableWidgetItem* line = item(list.at(i)->row(), 0);
  160. if (line->text() == q)
  161. {
  162. RemoveRow(list.at(i)->row());
  163. break;
  164. }
  165. }
  166. sortItems(0, Qt::AscendingOrder);
  167. }
  168. // QT table view messages
  169. void DHBreakpointsWidget::OnDoubleClicked(const QModelIndex& modelIdx)
  170. {
  171. // AZ_TracePrintf("BP", "OnDoubleClicked() %d, %d\n", modelIdx.row(), modelIdx.column());
  172. // magic number column #0 is the line number, 1 is the script file name
  173. QTableWidgetItem* line = item(modelIdx.row(), 0);
  174. QTableWidgetItem* file = item(modelIdx.row(), 1);
  175. LUAEditor::LUABreakpointRequestMessages::Bus::Broadcast(
  176. &LUAEditor::LUABreakpointRequestMessages::Bus::Events::RequestEditorFocus,
  177. AZStd::string(file->data(Qt::UserRole).toString().toUtf8().data()),
  178. line->data(Qt::DisplayRole).toInt());
  179. }
  180. void DHBreakpointsWidget::RemoveRow(int which)
  181. {
  182. // magic number column #0 is the line number, 1 is the script file name
  183. QTableWidgetItem* line = item(which, 0);
  184. QTableWidgetItem* file = item(which, 1);
  185. QByteArray fileName = file->data(Qt::UserRole).toString().toUtf8().data();
  186. int lineNumber = line->data(Qt::DisplayRole).toInt();
  187. LUAEditor::LUABreakpointRequestMessages::Bus::Broadcast(
  188. &LUAEditor::LUABreakpointRequestMessages::Bus::Events::RequestDeleteBreakpoint, AZStd::string(fileName.constData()), lineNumber);
  189. }