Remove breakpoint logic from codeeditor to ProgramViewer class
MODIFY
src/codeeditor.cpp
src/codeeditor.cpp
@@ -20,12 +20,9 @@ namespace Ripes {
CodeEditor::CodeEditor(QWidget* parent) : QPlainTextEdit(parent) {
m_lineNumberArea = new LineNumberArea(this);
- m_breakpointArea = new BreakpointArea(this);
-
- connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateSidebarWidth(int)));
-
- connect(this, SIGNAL(updateRequest(QRect, int)), this, SLOT(updateSidebar(QRect, int)));
+ connect(this, &QPlainTextEdit::blockCountChanged, this, &CodeEditor::updateSidebarWidth);
+ connect(this, &QPlainTextEdit::updateRequest, this, &CodeEditor::updateSidebar);
updateSidebarWidth(0);
// Set font for the entire widget. calls to fontMetrics() will get the
@@ -69,7 +66,7 @@ int CodeEditor::lineNumberAreaWidth() {
void CodeEditor::updateSidebarWidth(int /* newBlockCount */) {
// Set margins of the text edit area
- m_sidebarWidth = lineNumberAreaWidth() + m_breakpointArea->width();
+ m_sidebarWidth = lineNumberAreaWidth();
setViewportMargins(m_sidebarWidth, 0, 0, 0);
}
@@ -108,10 +105,6 @@ void CodeEditor::updateTooltip(int line, QString tip) {
}
}
-void CodeEditor::clearBreakpoints() {
- ProcessorHandler::get()->clearBreakpoints();
-}
-
bool CodeEditor::event(QEvent* event) {
// Override event handler for receiving tool tips
if (event->type() == QEvent::ToolTip) {
@@ -130,19 +123,11 @@ bool CodeEditor::event(QEvent* event) {
return QPlainTextEdit::event(event);
}
-void CodeEditor::enableBreakpointArea() {
- // Enables breakpoint area, and sets cursor for the breakpoint area to a "clickable" cursor
- m_breakpointAreaEnabled = true;
- m_breakpointArea->setCursor(Qt::PointingHandCursor);
-}
-
void CodeEditor::updateSidebar(const QRect& rect, int dy) {
if (dy) {
m_lineNumberArea->scroll(0, dy);
- m_breakpointArea->scroll(0, dy);
} else {
m_lineNumberArea->update(0, rect.y(), m_lineNumberArea->width(), rect.height());
- m_breakpointArea->update(0, rect.y(), m_breakpointArea->width(), rect.height());
}
if (rect.contains(viewport()->rect()))
@@ -152,11 +137,8 @@ void CodeEditor::updateSidebar(const QRect& rect, int dy) {
void CodeEditor::resizeEvent(QResizeEvent* e) {
QPlainTextEdit::resizeEvent(e);
- QRect cr = contentsRect();
- m_lineNumberArea->setGeometry(
- QRect(cr.left() + m_breakpointArea->width(), cr.top(), lineNumberAreaWidth(), cr.height()));
-
- m_breakpointArea->setGeometry(cr.left(), cr.top(), m_breakpointArea->width(), cr.height());
+ const QRect cr = contentsRect();
+ m_lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
}
void CodeEditor::highlightCurrentLine() {
@@ -200,46 +182,6 @@ void CodeEditor::lineNumberAreaPaintEvent(QPaintEvent* event) {
}
}
-void CodeEditor::breakpointAreaPaintEvent(QPaintEvent* event) {
- QPainter painter(m_breakpointArea);
-
- // When caret flashes in QPlainTextEdit a paint event is sent to this widget,
- // with a height of a line in the edit. We override this paint event by always
- // redrawing the visible breakpoint area
- auto area = m_breakpointArea->rect();
- QLinearGradient gradient = QLinearGradient(area.topLeft(), area.bottomRight());
- if (m_breakpointAreaEnabled) {
- gradient.setColorAt(0, QColor(Colors::FoundersRock).lighter(120));
- gradient.setColorAt(1, QColor(Colors::FoundersRock));
- } else {
- gradient.setColorAt(0, QColor(Qt::lightGray).lighter(120));
- gradient.setColorAt(1, QColor(Qt::lightGray).lighter(120));
- }
- painter.fillRect(area, gradient);
-
- if (m_breakpointAreaEnabled) {
- QTextBlock block = firstVisibleBlock();
- uint32_t address = ProcessorHandler::get()->getTextStart() +
- block.blockNumber() * ProcessorHandler::get()->currentISA()->bytes();
- int top = static_cast<int>(blockBoundingGeometry(block).translated(contentOffset()).top());
- int bottom = top + static_cast<int>(blockBoundingRect(block).height());
-
- while (block.isValid() && top <= event->rect().bottom()) {
- if (block.isVisible() && bottom >= event->rect().top()) {
- if (ProcessorHandler::get()->hasBreakpoint(address)) {
- painter.drawPixmap(m_breakpointArea->padding, top, m_breakpointArea->imageWidth,
- m_breakpointArea->imageHeight, m_breakpointArea->m_breakpoint);
- }
- }
-
- block = block.next();
- top = bottom;
- bottom = top + static_cast<int>(blockBoundingRect(block).height());
- address += ProcessorHandler::get()->currentISA()->bytes();
- }
- }
-}
-
void CodeEditor::setupSyntaxHighlighter() {
// Creates AsmHighlighter object and connects it to the current document
m_highlighter = new SyntaxHighlighter(document());
@@ -261,73 +203,4 @@ void CodeEditor::setupSyntaxHighlighter() {
});
}
-long CodeEditor::addressForPos(const QPoint& pos) const {
- if (!m_breakpointAreaEnabled)
- return -1;
-
- // Get line height
- QTextBlock block = firstVisibleBlock();
-
- const auto height = blockBoundingRect(block).height();
-
- // Find block index in the codeeditor
- int index;
- if (block == document()->findBlockByLineNumber(0)) {
- index = static_cast<int>((pos.y() - contentOffset().y()) / height);
- } else {
- index = static_cast<int>((pos.y() + contentOffset().y()) / height);
- }
- // Get actual block index
- while (index > 0) {
- block = block.next();
- index--;
- }
- if (!block.isValid())
- return -1;
-
- // Toggle breakpoint
- return block.blockNumber() * ProcessorHandler::get()->currentISA()->bytes() +
- ProcessorHandler::get()->getTextStart();
-}
-
-bool CodeEditor::hasBreakpoint(const QPoint& pos) const {
- if (!m_breakpointAreaEnabled)
- return false;
-
- return ProcessorHandler::get()->hasBreakpoint(static_cast<unsigned>(addressForPos(pos)));
-}
-
-void CodeEditor::breakpointClick(const QPoint& pos) {
- if (m_breakpointAreaEnabled) {
- // Toggle breakpoint
- auto address = addressForPos(pos);
- if (!(address < 0)) {
- ProcessorHandler::get()->toggleBreakpoint(static_cast<unsigned>(address));
- repaint();
- }
- }
-}
-
-// -------------- breakpoint area ----------------------------------
-
-BreakpointArea::BreakpointArea(CodeEditor* editor) : QWidget(editor) {
- codeEditor = editor;
-}
-
-void BreakpointArea::contextMenuEvent(QContextMenuEvent* event) {
- // setup context menu
- QMenu contextMenu;
-
- // Create and connect actions for removing and setting breakpoints
- auto* toggleAction = contextMenu.addAction("Toggle breakpoint");
- auto* removeAllAction = contextMenu.addAction("Remove all breakpoints");
-
- connect(toggleAction, &QAction::triggered, [=] { codeEditor->breakpointClick(event->pos()); });
- connect(removeAllAction, &QAction::triggered, [=] {
- codeEditor->clearBreakpoints();
- repaint();
- });
-
- contextMenu.exec(event->globalPos());
-}
} // namespace Ripes

MODIFY
src/codeeditor.h
src/codeeditor.h
namespace Ripes {
class LineNumberArea;
-class BreakpointArea;
class CodeEditor : public QPlainTextEdit {
Q_OBJECT
@@ -24,15 +23,9 @@ public:
CodeEditor(QWidget* parent = nullptr);
void lineNumberAreaPaintEvent(QPaintEvent* event);
- void breakpointAreaPaintEvent(QPaintEvent* event);
- void breakpointClick(const QPoint& pos);
- bool hasBreakpoint(const QPoint& pos) const;
- long addressForPos(const QPoint& pos) const;
- void clearBreakpoints();
int lineNumberAreaWidth();
void setupSyntaxHighlighter();
void setupChangedTimer();
- void enableBreakpointArea();
void reset() {
m_highlighter->reset();
m_tooltipForLine.clear();
@@ -56,7 +49,6 @@ private slots:
private:
SyntaxHighlighter* m_highlighter;
LineNumberArea* m_lineNumberArea;
- BreakpointArea* m_breakpointArea;
int m_sidebarWidth;
bool m_syntaxChecking = false;
@@ -64,7 +56,7 @@ private:
QMap<int, QString> m_tooltipForLine;
- QFont m_font = font();
+ QFont m_font;
// A timer is needed for only catching one of the multiple wheel events that
// occur on a regular mouse scroll
@@ -74,15 +66,6 @@ private:
bool eventFilter(QObject* observed, QEvent* event) override;
};
-// base class for side area widgets that are attached to the code editor
-class SideArea : public QWidget {
- Q_OBJECT
-public:
- SideArea(QWidget* parent = nullptr);
-
-private:
-};
-
class LineNumberArea : public QWidget {
public:
LineNumberArea(CodeEditor* editor) : QWidget(editor) { codeEditor = editor; }
@@ -101,40 +84,4 @@ private:
CodeEditor* codeEditor;
};
-class BreakpointArea : public QWidget {
-public:
- BreakpointArea(CodeEditor* editor);
-
- QSize sizeHint() const override { return QSize(width(), 0); }
- int width() const { return imageWidth + padding * 2; }
- QSize breakpointSize() { return QSize(imageWidth, imageHeight); }
-
- int imageWidth = 16;
- int imageHeight = 16;
- int padding = 3; // padding on each side of the breakpoint
- QPixmap m_breakpoint = QPixmap(":/icons/breakpoint_enabled.png").scaled(imageWidth, imageHeight);
- QPixmap m_breakpoint_disabled = QPixmap(":/icons/breakpoint_disabled.png").scaled(imageWidth, imageWidth);
-
- void setBlockHeight(int height) { m_blockHeight = height; }
-
-protected:
- void paintEvent(QPaintEvent* event) override { codeEditor->breakpointAreaPaintEvent(event); }
-
- void contextMenuEvent(QContextMenuEvent* event) override;
-
-private:
- CodeEditor* codeEditor;
- int m_blockHeight = 0;
-
- void mouseReleaseEvent(QMouseEvent* event) override {
- if (event->button() == Qt::LeftButton) {
- codeEditor->breakpointClick(event->pos());
- }
- }
-
- void wheelEvent(QWheelEvent* event) override {
- codeEditor->verticalScrollBar()->setValue(codeEditor->verticalScrollBar()->value() +
- (-event->angleDelta().y()) / 30);
- }
-};
} // namespace Ripes

MODIFY
src/edittab.cpp
src/edittab.cpp
@@ -24,10 +24,7 @@ EditTab::EditTab(QToolBar* toolbar, QWidget* parent) : RipesTab(toolbar, parent)
// translation is complete
m_ui->assemblyedit->setupSyntaxHighlighter();
m_ui->assemblyedit->setupChangedTimer();
- m_ui->binaryedit->setReadOnly(true);
-
- // enable breakpoint area for the translated code only
- m_ui->binaryedit->enableBreakpointArea();
+ m_ui->programViewer->setReadOnly(true);
m_assembler = std::make_unique<Assembler>();
@@ -116,7 +113,7 @@ void EditTab::setAssemblyText(const QString& text) {
void EditTab::enableAssemblyInput() {
// Clear currently loaded binary/ELF program
m_activeProgram = Program();
- m_ui->binaryedit->clear();
+ m_ui->programViewer->clear();
enableEditor();
m_editorEnabled = true;
emit editorStateChanged(m_editorEnabled);
@@ -129,7 +126,7 @@ void EditTab::setDisassemblerText() {
auto text = m_ui->disassembledViewButton->isChecked() ? Parser::getParser()->disassemble(textSection->data)
: Parser::getParser()->binarize(textSection->data);
- m_ui->binaryedit->setPlainText(text);
+ m_ui->programViewer->setPlainText(text);
}
void EditTab::enableEditor() {
@@ -186,6 +183,25 @@ bool EditTab::loadElfFile(Program& program, QFile& file) {
section.address = elfSection->get_address();
// QByteArray performs a deep copy of the data when the data array is initialized at construction
section.data = QByteArray(elfSection->get_data(), static_cast<int>(elfSection->get_size()));
+
+ if (elfSection->get_type() == SHT_SYMTAB) {
+ // Collect function symbols
+ const ELFIO::symbol_section_accessor symbols(reader, elfSection);
+ for (unsigned int j = 0; j < symbols.get_symbols_num(); ++j) {
+ std::string name;
+ ELFIO::Elf64_Addr value;
+ ELFIO::Elf_Xword size;
+ unsigned char bind;
+ unsigned char type;
+ ELFIO::Elf_Half section_index;
+ unsigned char other;
+ symbols.get_symbol(j, name, value, size, bind, type, section_index, other);
+
+ if (type != STT_FUNC)
+ continue;
+ program.symbols[value] = QString::fromStdString(name);
+ }
+ }
}
program.entryPoint = reader.get_entry();

MODIFY
src/edittab.ui
src/edittab.ui
</layout>
</item>
<item>
- <widget class="CodeEditor" name="binaryedit"/>
+ <widget class="ProgramViewer" name="programViewer"/>
</item>
</layout>
</widget>
<extends>QPlainTextEdit</extends>
<header>codeeditor.h</header>
</customwidget>
+ <customwidget>
+ <class>ProgramViewer</class>
+ <extends>QPlainTextEdit</extends>
+ <header>programviewer.h</header>
+ </customwidget>
</customwidgets>
<resources/>
<connections/>

MODIFY
src/program.h
src/program.h
@@ -32,6 +32,7 @@ struct ProgramSection {
struct Program {
unsigned long entryPoint = 0;
std::vector<ProgramSection> sections;
+ std::map<unsigned long, QString> symbols;
const ProgramSection* getSection(const QString& name) const {
const auto secIter =

ADD
src/programviewer.cpp
+#include "programviewer.h"
+
+#include "defines.h"
+#include "processorhandler.h"
+
+#include <QMenu>
+#include <QAction>
+#include <QTextBlock>
+#include <QEvent>
+#include <QApplication>
+
+namespace Ripes {
+
+ProgramViewer::ProgramViewer(QWidget* parent) : QPlainTextEdit(parent)
+{
+ m_breakpointArea = new BreakpointArea(this);
+
+ connect(this, &QPlainTextEdit::blockCountChanged, this, &ProgramViewer::updateSidebarWidth);
+ connect(this, &QPlainTextEdit::updateRequest, this, &ProgramViewer::updateSidebar);
+ updateSidebarWidth(0);
+
+
+ // Set font for the entire widget. calls to fontMetrics() will get the
+ // dimensions of the currently set font
+ m_font = QFont("Inconsolata", 11);
+ setFont(m_font);
+ m_fontTimer.setSingleShot(true);
+}
+
+bool ProgramViewer::eventFilter(QObject*, QEvent* event) {
+ // Event filter for catching ctrl+Scroll events, for text resizing
+ if (event->type() == QEvent::Wheel && QApplication::keyboardModifiers() == Qt::ControlModifier) {
+ auto wheelEvent = static_cast<QWheelEvent*>(event);
+ // Since multiple wheelevents are issued on a scroll,
+ // start a timer to only catch the first one
+
+ // change font size
+ if (!m_fontTimer.isActive()) {
+ if (wheelEvent->angleDelta().y() > 0) {
+ if (m_font.pointSize() < 30)
+ m_font.setPointSize(m_font.pointSize() + 1);
+ } else {
+ if (m_font.pointSize() > 6)
+ m_font.setPointSize(m_font.pointSize() - 1);
+ }
+ m_fontTimer.start(50);
+ }
+ setFont(m_font);
+ return true;
+ }
+
+ return false;
+}
+
+void ProgramViewer::clearBreakpoints() {
+ ProcessorHandler::get()->clearBreakpoints();
+}
+
+void ProgramViewer::resizeEvent(QResizeEvent* e) {
+ QPlainTextEdit::resizeEvent(e);
+
+ const QRect cr = contentsRect();
+ m_breakpointArea->setGeometry(cr.left(), cr.top(), m_breakpointArea->width(), cr.height());
+}
+
+void ProgramViewer::updateSidebar(const QRect& rect, int dy) {
+
+ m_breakpointArea->update(0, rect.y(), m_breakpointArea->width(), rect.height());
+
+
+ if (rect.contains(viewport()->rect()))
+ updateSidebarWidth(0);
+}
+
+void ProgramViewer::updateSidebarWidth(int /* newBlockCount */) {
+ // Set margins of the text edit area
+ m_sidebarWidth = m_breakpointArea->width();
+ setViewportMargins(m_sidebarWidth, 0, 0, 0);
+}
+
+void ProgramViewer::breakpointAreaPaintEvent(QPaintEvent* event) {
+ QPainter painter(m_breakpointArea);
+
+ // When caret flashes in QPlainTextEdit a paint event is sent to this widget,
+ // with a height of a line in the edit. We override this paint event by always
+ // redrawing the visible breakpoint area
+ auto area = m_breakpointArea->rect();
+ QLinearGradient gradient = QLinearGradient(area.topLeft(), area.bottomRight());
+ gradient.setColorAt(0, QColor(Colors::FoundersRock).lighter(120));
+ gradient.setColorAt(1, QColor(Colors::FoundersRock));
+
+ painter.fillRect(area, gradient);
+
+ QTextBlock block = firstVisibleBlock();
+ uint32_t address = ProcessorHandler::get()->getTextStart() +
+ block.blockNumber() * ProcessorHandler::get()->currentISA()->bytes();
+ int top = static_cast<int>(blockBoundingGeometry(block).translated(contentOffset()).top());
+ int bottom = top + static_cast<int>(blockBoundingRect(block).height());
+
+ while (block.isValid() && top <= event->rect().bottom()) {
+ if (block.isVisible() && bottom >= event->rect().top()) {
+ if (ProcessorHandler::get()->hasBreakpoint(address)) {
+ painter.drawPixmap(m_breakpointArea->padding, top, m_breakpointArea->imageWidth,
+ m_breakpointArea->imageHeight, m_breakpointArea->m_breakpoint);
+ }
+ }
+
+ block = block.next();
+ top = bottom;
+ bottom = top + static_cast<int>(blockBoundingRect(block).height());
+ address += ProcessorHandler::get()->currentISA()->bytes();
+ }
+
+}
+
+
+long ProgramViewer::addressForPos(const QPoint& pos) const {
+ // Get line height
+ QTextBlock block = firstVisibleBlock();
+
+ const auto height = blockBoundingRect(block).height();
+
+ // Find block index in the ProgramViewer
+ int index;
+ if (block == document()->findBlockByLineNumber(0)) {
+ index = static_cast<int>((pos.y() - contentOffset().y()) / height);
+ } else {
+ index = static_cast<int>((pos.y() + contentOffset().y()) / height);
+ }
+ // Get actual block index
+ while (index > 0) {
+ block = block.next();
+ index--;
+ }
+ if (!block.isValid())
+ return -1;
+
+ // Toggle breakpoint
+ return block.blockNumber() * ProcessorHandler::get()->currentISA()->bytes() +
+ ProcessorHandler::get()->getTextStart();
+}
+
+bool ProgramViewer::hasBreakpoint(const QPoint& pos) const {
+ return ProcessorHandler::get()->hasBreakpoint(static_cast<unsigned>(addressForPos(pos)));
+}
+
+void ProgramViewer::breakpointClick(const QPoint& pos) {
+ const auto address = addressForPos(pos);
+ if (!(address < 0)) {
+ ProcessorHandler::get()->toggleBreakpoint(static_cast<unsigned>(address));
+ repaint();
+ }
+}
+
+// -------------- breakpoint area ----------------------------------
+
+BreakpointArea::BreakpointArea(ProgramViewer* viewer) : QWidget(viewer) {
+ m_programViewer = viewer;
+ setCursor(Qt::PointingHandCursor);
+}
+
+void BreakpointArea::contextMenuEvent(QContextMenuEvent* event) {
+ // setup context menu
+ QMenu contextMenu;
+
+ // Create and connect actions for removing and setting breakpoints
+ auto* toggleAction = contextMenu.addAction("Toggle breakpoint");
+ auto* removeAllAction = contextMenu.addAction("Remove all breakpoints");
+
+ connect(toggleAction, &QAction::triggered, [=] { m_programViewer->breakpointClick(event->pos()); });
+ connect(removeAllAction, &QAction::triggered, [=] {
+ m_programViewer->clearBreakpoints();
+ repaint();
+ });
+
+ contextMenu.exec(event->globalPos());
+}
+
+}

ADD
src/programviewer.h
+#pragma once
+
+#include <QFont>
+#include <QObject>
+#include <QPlainTextEdit>
+#include <QScrollBar>
+#include <QTimer>
+
+namespace Ripes {
+
+class BreakpointArea;
+
+class ProgramViewer : public QPlainTextEdit {
+ Q_OBJECT
+public:
+ ProgramViewer(QWidget* parent = nullptr);
+
+ void breakpointAreaPaintEvent(QPaintEvent* event);
+ void breakpointClick(const QPoint& pos);
+ bool hasBreakpoint(const QPoint& pos) const;
+ long addressForPos(const QPoint& pos) const;
+ void clearBreakpoints();
+
+protected:
+ void resizeEvent(QResizeEvent* event) override;
+
+private slots:
+ void updateSidebar(const QRect&, int);
+ void updateSidebarWidth(int newBlockCount);
+
+private:
+ // A timer is needed for only catching one of the multiple wheel events that
+ // occur on a regular mouse scroll
+ QTimer m_fontTimer;
+ QTimer m_changeTimer;
+
+ QFont m_font;
+ int m_sidebarWidth;
+
+ BreakpointArea* m_breakpointArea;
+
+ bool eventFilter(QObject* observed, QEvent* event) override;
+};
+
+class BreakpointArea : public QWidget {
+public:
+ BreakpointArea(ProgramViewer* viewer);
+
+ QSize sizeHint() const override { return QSize(width(), 0); }
+ int width() const { return imageWidth + padding * 2; }
+ QSize breakpointSize() { return QSize(imageWidth, imageHeight); }
+
+ int imageWidth = 16;
+ int imageHeight = 16;
+ int padding = 3; // padding on each side of the breakpoint
+ QPixmap m_breakpoint = QPixmap(":/icons/breakpoint_enabled.png").scaled(imageWidth, imageHeight);
+ QPixmap m_breakpoint_disabled = QPixmap(":/icons/breakpoint_disabled.png").scaled(imageWidth, imageWidth);
+
+ void setBlockHeight(int height) { m_blockHeight = height; }
+
+protected:
+ void paintEvent(QPaintEvent* event) override { m_programViewer->breakpointAreaPaintEvent(event); }
+
+ void contextMenuEvent(QContextMenuEvent* event) override;
+
+private:
+ ProgramViewer* m_programViewer;
+ int m_blockHeight = 0;
+
+ void mouseReleaseEvent(QMouseEvent* event) override {
+ if (event->button() == Qt::LeftButton) {
+ m_programViewer->breakpointClick(event->pos());
+ }
+ }
+
+ void wheelEvent(QWheelEvent* event) override {
+ m_programViewer->verticalScrollBar()->setValue(m_programViewer->verticalScrollBar()->value() +
+ (-event->angleDelta().y()) / 30);
+ }
+};
+
+} // namespace Ripes

Remove breakpoint logic from codeeditor to ProgramViewer class