diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 5834a485..ba7a0acb 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -1,6 +1,8 @@ add_files( APP_FILES + qt/element/QtCodeSnippet.cpp + qt/element/QtCodeSnippet.h qt/element/QtMainWindow.cpp qt/element/QtMainWindow.h @@ -12,7 +14,7 @@ add_files( qt/view/graphElements/QtGraphNode.cpp qt/view/graphElements/QtGraphNode.h - + qt/view/QtCodeView.cpp qt/view/QtCodeView.h qt/view/QtGraphView.cpp diff --git a/src/app/qt/element/QtCodeSnippet.cpp b/src/app/qt/element/QtCodeSnippet.cpp new file mode 100644 index 00000000..94e61453 --- /dev/null +++ b/src/app/qt/element/QtCodeSnippet.cpp @@ -0,0 +1,185 @@ +#include "qt/element/QtCodeSnippet.h" + +#include + +#include "data/location/TokenLocation.h" +#include "data/location/TokenLocationFile.h" +#include "data/location/TokenLocationLine.h" +#include "qt/view/QtCodeView.h" + +QtCodeSnippet::LineNumberArea::LineNumberArea(QtCodeSnippet *codeSnippet) + : QWidget(codeSnippet) + , m_codeSnippet(codeSnippet) +{ +} + +QtCodeSnippet::LineNumberArea::~LineNumberArea() +{ +} + +QSize QtCodeSnippet::LineNumberArea::sizeHint() const +{ + return QSize(m_codeSnippet->lineNumberAreaWidth(), 0); +} + +void QtCodeSnippet::LineNumberArea::paintEvent(QPaintEvent *event) +{ + m_codeSnippet->lineNumberAreaPaintEvent(event); +} + + +QtCodeSnippet::QtCodeSnippet(QtCodeView* parentView, int startLineNumber, QWidget *parent) + : QPlainTextEdit(parent) + , m_parentView(parentView) + , m_startLineNumber(startLineNumber) +{ + m_lineNumberArea = new LineNumberArea(this); + + connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int))); + connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int))); + connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(clickTokenLocation())); + + updateLineNumberAreaWidth(0); +} + +QtCodeSnippet::~QtCodeSnippet() +{ +} + +void QtCodeSnippet::lineNumberAreaPaintEvent(QPaintEvent *event) +{ + QPainter painter(m_lineNumberArea); + painter.fillRect(event->rect(), QColor(225,225,225)); + + QTextBlock block = firstVisibleBlock(); + int blockNumber = block.blockNumber(); + int top = static_cast(blockBoundingGeometry(block).translated(contentOffset()).top()); + int bottom = top + static_cast(blockBoundingRect(block).height()); + + while (block.isValid() && top <= event->rect().bottom()) + { + if (block.isVisible() && bottom >= event->rect().top()) + { + QString number = QString::number(blockNumber + m_startLineNumber); + painter.setPen(Qt::black); + painter.drawText(0, top, m_lineNumberArea->width() - 1, fontMetrics().height(), Qt::AlignRight, number); + } + + block = block.next(); + top = bottom; + bottom = top + static_cast(blockBoundingRect(block).height()); + blockNumber++; + } +} + +int QtCodeSnippet::lineNumberAreaWidth() +{ + int digits = 1; + int max = qMax(1, m_startLineNumber + blockCount()); + + while (max >= 10) + { + max /= 10; + digits++; + } + + int width = 2 + fontMetrics().width(QLatin1Char('9')) * digits; + return width; +} + +void QtCodeSnippet::annotateText(const TokenLocationFile& locationFile) +{ + QList extraSelections; + + for (const TokenLocationFile::TokenLocationLinePairType& lineItem : locationFile.getTokenLocationLines()) + { + for (const TokenLocationLine::TokenLocationPairType& locationItem : lineItem.second->getTokenLocations()) + { + TokenLocation* location = locationItem.second.get(); + if (!location->isStartTokenLocation()) + { + continue; + } + + Annotation annotation; + annotation.start = toTextEditPosition(location->getLineNumber(), location->getColumnNumber() - 1); + annotation.end = toTextEditPosition( + location->getEndTokenLocation()->getLineNumber(), + location->getEndTokenLocation()->getColumnNumber()); + annotation.tokenId = location->getTokenId(); + m_annotations.push_back(annotation); + + QTextEdit::ExtraSelection selection; + + QColor color = QColor(Qt::red); + color.setAlphaF(0.3f); + selection.format.setBackground(color); + + selection.cursor = textCursor(); + selection.cursor.clearSelection(); + selection.cursor.setPosition(annotation.start); + selection.cursor.setPosition(annotation.end, QTextCursor::KeepAnchor); + + extraSelections.append(selection); + } + } + + setExtraSelections(extraSelections); +} + +void QtCodeSnippet::resizeEvent(QResizeEvent *e) +{ + QPlainTextEdit::resizeEvent(e); + + QRect cr = contentsRect(); + m_lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height())); +} + +void QtCodeSnippet::updateLineNumberAreaWidth(int /* newBlockCount */) +{ + setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); +} + +void QtCodeSnippet::updateLineNumberArea(const QRect &rect, int dy) +{ + if (dy) + { + m_lineNumberArea->scroll(0, dy); + } + else + { + m_lineNumberArea->update(0, rect.y(), m_lineNumberArea->width(), rect.height()); + } + + if (rect.contains(viewport()->rect())) + { + updateLineNumberAreaWidth(0); + } +} + +void QtCodeSnippet::clickTokenLocation() +{ + int clickPosition = textCursor().position(); + for (Annotation annotation : m_annotations) + { + if (clickPosition >= annotation.start && clickPosition <= annotation.end) + { + m_parentView->activateToken(annotation.tokenId); + return; + } + } +} + +int QtCodeSnippet::toTextEditPosition(int lineNumber, int columnNumber) const +{ + lineNumber -= m_startLineNumber - 1; + + int position = 0; + for (int i = 0; i < lineNumber - 1; i++) + { + position += document()->findBlockByLineNumber(i).length(); + } + position += columnNumber; + + return position; +} diff --git a/src/app/qt/element/QtCodeSnippet.h b/src/app/qt/element/QtCodeSnippet.h new file mode 100644 index 00000000..f1e66f70 --- /dev/null +++ b/src/app/qt/element/QtCodeSnippet.h @@ -0,0 +1,71 @@ +#ifndef QT_CODE_SNIPPET_H +#define QT_CODE_SNIPPET_H + +#include + +#include +#include + +#include "utility/types.h" + +class QPaintEvent; +class QResizeEvent; +class QSize; +class QtCodeView; +class QWidget; +class TokenLocationFile; + +class QtCodeSnippet: public QPlainTextEdit +{ + Q_OBJECT + +public: + class LineNumberArea: public QWidget + { + public: + LineNumberArea(QtCodeSnippet *codeSnippet); + virtual ~LineNumberArea(); + + QSize sizeHint() const; + + protected: + void paintEvent(QPaintEvent *event); + + private: + QtCodeSnippet *m_codeSnippet; + }; + + QtCodeSnippet(QtCodeView* parentView, int startLineNumber, QWidget *parent = 0); + virtual ~QtCodeSnippet(); + + void lineNumberAreaPaintEvent(QPaintEvent *event); + int lineNumberAreaWidth(); + + void annotateText(const TokenLocationFile& locationFile); + +protected: + void resizeEvent(QResizeEvent *event); + +private slots: + void updateLineNumberAreaWidth(int newBlockCount); + void updateLineNumberArea(const QRect &, int); + void clickTokenLocation(); + +private: + struct Annotation + { + int start; + int end; + Id tokenId; + }; + + int toTextEditPosition(int lineNumber, int columnNumber) const; + + QWidget *m_lineNumberArea; + QtCodeView* m_parentView; + + const int m_startLineNumber; + std::vector m_annotations; +}; + +#endif // QT_CODE_SNIPPET_H diff --git a/src/app/qt/view/QtCodeView.cpp b/src/app/qt/view/QtCodeView.cpp index 776ea774..55041f57 100644 --- a/src/app/qt/view/QtCodeView.cpp +++ b/src/app/qt/view/QtCodeView.cpp @@ -1,7 +1,10 @@ #include "qt/view/QtCodeView.h" +#include #include +#include "data/location/TokenLocationFile.h" +#include "qt/element/QtCodeSnippet.h" #include "qt/QtWidgetWrapper.h" #include "qt/utility/QtHighLighter.h" #include "qt/utility/utilityQt.h" @@ -9,7 +12,7 @@ QtCodeView::QtCodeView(ViewLayout* viewLayout) : CodeView(viewLayout) , m_clearCodeSnippetsFunctor(std::bind(&QtCodeView::doClearCodeSnippets, this)) - , m_addCodeSnippetFunctor(std::bind(&QtCodeView::doAddCodeSnippet, this, std::placeholders::_1)) + , m_addCodeSnippetFunctor(std::bind(&QtCodeView::doAddCodeSnippet, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)) { } @@ -37,9 +40,9 @@ void QtCodeView::initGui() m_font.setPointSize(10); } -void QtCodeView::addCodeSnippet(std::string str) +void QtCodeView::addCodeSnippet(const std::string& str, const TokenLocationFile& locationFile, int startLineNumber) { - m_addCodeSnippetFunctor(str); + m_addCodeSnippetFunctor(str, locationFile, startLineNumber); } void QtCodeView::clearCodeSnippets() @@ -47,17 +50,22 @@ void QtCodeView::clearCodeSnippets() m_clearCodeSnippetsFunctor(); } -void QtCodeView::doAddCodeSnippet(std::string str) +void QtCodeView::activateToken(Id tokenId) const +{ + std::cout << "TODO: activate Token: " << tokenId << std::endl; +} + +void QtCodeView::doAddCodeSnippet(const std::string& str, const TokenLocationFile& locationFile, int startLineNumber) { std::shared_ptr snippet = std::make_shared(); - snippet->textField = std::make_shared(); + snippet->textField = std::make_shared(this, startLineNumber); snippet->textField->setReadOnly(true); snippet->textField->setFont(m_font); snippet->highlighter = std::make_shared(snippet->textField->document()); - snippet->textField->setPlainText(QString::fromUtf8(str.c_str())); + snippet->textField->annotateText(locationFile); QWidget* widget = QtWidgetWrapper::getWidgetOfView(this); widget->layout()->addWidget(snippet->textField.get()); diff --git a/src/app/qt/view/QtCodeView.h b/src/app/qt/view/QtCodeView.h index b481793f..6a034ede 100644 --- a/src/app/qt/view/QtCodeView.h +++ b/src/app/qt/view/QtCodeView.h @@ -8,7 +8,9 @@ #include "component/view/CodeView.h" #include "qt/utility/QtThreadedFunctor.h" +#include "utility/types.h" +class QtCodeSnippet; class QtHighlighter; class QTextEdit; @@ -23,24 +25,26 @@ public: virtual void initGui(); // CodeView implementation - virtual void addCodeSnippet(std::string str); + virtual void addCodeSnippet(const std::string& str, const TokenLocationFile& locationFile, int startLineNumber); virtual void clearCodeSnippets(); + void activateToken(Id tokenId) const; + private: struct Snippet { - std::shared_ptr textField; + std::shared_ptr textField; std::shared_ptr highlighter; }; std::vector> m_snippets; - void doAddCodeSnippet(std::string str); + void doAddCodeSnippet(const std::string& str, const TokenLocationFile& locationFile, int startLineNumber); void doClearCodeSnippets(); QFont m_font; QtThreadedFunctor m_clearCodeSnippetsFunctor; - QtThreadedFunctor m_addCodeSnippetFunctor; + QtThreadedFunctor m_addCodeSnippetFunctor; }; # endif // QT_CODE_VIEW_H diff --git a/src/lib/component/ComponentManager.cpp b/src/lib/component/ComponentManager.cpp index cf39d5a8..e3a01d29 100644 --- a/src/lib/component/ComponentManager.cpp +++ b/src/lib/component/ComponentManager.cpp @@ -4,6 +4,8 @@ #include "component/view/GraphView.h" #include "component/view/ViewLayout.h" +#include "data/location/TokenLocationFile.h" + std::shared_ptr ComponentManager::create( GuiFactory* guiFactory, ViewLayout* viewLayout, @@ -33,8 +35,36 @@ void ComponentManager::setup() m_components.push_back(codeComponent); CodeView* codeView = codeComponent->getView(); - codeView->addCodeSnippet("class HelloWorld;"); - codeView->addCodeSnippet("static int n = 42; // the answer."); + + std::string code = + "class HelloWorld;\n" + "static int n = 42; // the answer.\n" + "\n" + "int sum(int a, int b)\n" + "{\n" + " return a + b;\n" + "}\n"; + + TokenLocationFile locationFile("test.cpp"); + locationFile.addTokenLocation(1, 1, 7, 1, 16); + locationFile.addTokenLocation(2, 2, 8, 2, 10); + locationFile.addTokenLocation(3, 2, 12, 2, 12); + locationFile.addTokenLocation(4, 4, 1, 7, 1); + + codeView->addCodeSnippet(code, locationFile, 1); + + std::string code2 = + "const char* name = new char[10];\n" + "\n" + "name = \"MetaVizz\\0\";"; + + TokenLocationFile locationFile2("test.cpp"); + locationFile2.addTokenLocation(5, 123, 1, 123, 11); + locationFile2.addTokenLocation(6, 123, 13, 123, 16); + locationFile2.addTokenLocation(7, 125, 1, 125, 4); + + codeView->addCodeSnippet(code2, locationFile2, 123); + // codeView->clearCodeSnippets(); } diff --git a/src/lib/component/controller/CodeController.cpp b/src/lib/component/controller/CodeController.cpp index 4650d36e..ab983f16 100644 --- a/src/lib/component/controller/CodeController.cpp +++ b/src/lib/component/controller/CodeController.cpp @@ -24,7 +24,7 @@ void CodeController::setActiveTokenLocationId(Id id) { getView()->clearCodeSnippets(); std::shared_ptr textAccess = TextAccess::createFromFile(tokenLocation->getFilePath()); - getView()->addCodeSnippet(textAccess->getText()); + // getView()->addCodeSnippet(textAccess->getText()); } } diff --git a/src/lib/component/view/CodeView.h b/src/lib/component/view/CodeView.h index 70135ff9..bcce8e6f 100644 --- a/src/lib/component/view/CodeView.h +++ b/src/lib/component/view/CodeView.h @@ -4,6 +4,7 @@ #include "component/view/View.h" class CodeController; +class TokenLocationFile; class CodeView: public View { @@ -13,7 +14,7 @@ public: virtual std::string getName() const; - virtual void addCodeSnippet(std::string str) = 0; + virtual void addCodeSnippet(const std::string& str, const TokenLocationFile& locationFile, int startLineNumber) = 0; virtual void clearCodeSnippets() = 0; private: