diff --git a/DRAMSys/traceAnalyzer/businessObjects/comment.h b/DRAMSys/traceAnalyzer/businessObjects/comment.h index c52753f8..91d8860b 100644 --- a/DRAMSys/traceAnalyzer/businessObjects/comment.h +++ b/DRAMSys/traceAnalyzer/businessObjects/comment.h @@ -59,6 +59,16 @@ public: { text = newText; } + bool isSelected(traceTime mouseTime, traceTime clk) + { + // Increase selectable area of comments + traceTime offset = static_cast(0.35 * clk); + + if (time >= (mouseTime - offset) && time <= (mouseTime + offset)) + return true; + + return false; + } }; #endif // COMMENT_H diff --git a/DRAMSys/traceAnalyzer/presentation/commenttreewidget.cpp b/DRAMSys/traceAnalyzer/presentation/commenttreewidget.cpp index ebdf84db..399354a2 100644 --- a/DRAMSys/traceAnalyzer/presentation/commenttreewidget.cpp +++ b/DRAMSys/traceAnalyzer/presentation/commenttreewidget.cpp @@ -41,12 +41,10 @@ #include #include #include -#include -#include CommentTreeWidget::CommentTreeWidget(QWidget *parent) : QTreeWidget(parent), deleteComment(new QAction("Delete comment", this)), - changeCommentText(new QAction("Change comment text", this)) + editCommentText(new QAction("Edit comment text", this)) { connect(deleteComment, &QAction::triggered, this, [this](){ for (QTreeWidgetItem *item : selectedItems()) @@ -56,25 +54,25 @@ CommentTreeWidget::CommentTreeWidget(QWidget *parent) : QTreeWidget(parent), } }); - connect(changeCommentText, &QAction::triggered, this, [this](){ + connect(editCommentText, &QAction::triggered, this, [this](){ for (QTreeWidgetItem *item : selectedItems()) { CommentTreeItem *commentItem = static_cast(item); - - bool ok; - QString newText = QInputDialog::getText(this, QString("Change comment text"), - QString("Change comment text"), QLineEdit::Normal, - commentItem->Text(), &ok); - - if (ok) - { - deleteComment->trigger(); - navigator->insertComment({commentItem->Time(), newText}); - } - - commentsChanged(); + navigator->editCommentAtTime(commentItem->Time()); } }); + + connect(this, &QTreeWidget::itemSelectionChanged, this, [this](){ + std::vector> selectedComments; + + for (auto &item : selectedItems()) + { + CommentTreeItem *commentItem = static_cast(item); + selectedComments.push_back(commentItem->getComment()); + } + + Q_EMIT selectedCommentsChanged(selectedComments); + }); } void CommentTreeWidget::init(TraceNavigator *navigator) @@ -103,7 +101,7 @@ void CommentTreeWidget::commentsChanged() void CommentTreeWidget::printComments() { for (const auto &pair : navigator->getComments()) { - const Comment &comment = pair.second; + const std::shared_ptr comment = pair.second; QTreeWidgetItem *item = new CommentTreeItem(this, comment); addTopLevelItem(item); } @@ -118,17 +116,19 @@ void CommentTreeWidget::itemDoubleClicked(QTreeWidgetItem *item, int /*column*/) void CommentTreeWidget::ContextMenuRequested(QPoint point) { + if (selectedItems().isEmpty()) + return; + QMenu contextMenu(this); - contextMenu.addActions({deleteComment, changeCommentText}); + contextMenu.addActions({editCommentText, deleteComment}); contextMenu.exec(mapToGlobal(point)); } - CommentTreeWidget::CommentTreeItem::CommentTreeItem(QTreeWidget *parent, - const Comment &comment): QTreeWidgetItem(parent), comment(comment) + const std::shared_ptr comment): QTreeWidgetItem(parent), comment(comment) { - this->setText(0, prettyFormatTime(comment.Time())); - this->setText(1, comment.Text()); + this->setText(0, prettyFormatTime(comment->Time())); + this->setText(1, comment->Text()); } void CommentTreeWidget::keyPressEvent(QKeyEvent *event) @@ -136,7 +136,7 @@ void CommentTreeWidget::keyPressEvent(QKeyEvent *event) if (event->key() == Qt::Key_Delete) deleteComment->trigger(); else if (event->key() == Qt::Key_F2) - changeCommentText->trigger(); + editCommentText->trigger(); QTreeWidget::keyPressEvent(event); } diff --git a/DRAMSys/traceAnalyzer/presentation/commenttreewidget.h b/DRAMSys/traceAnalyzer/presentation/commenttreewidget.h index 7429f75e..c5c63bad 100644 --- a/DRAMSys/traceAnalyzer/presentation/commenttreewidget.h +++ b/DRAMSys/traceAnalyzer/presentation/commenttreewidget.h @@ -43,6 +43,7 @@ #include #include "businessObjects/comment.h" #include "tracenavigator.h" +#include "businessObjects/tracetime.h" class CommentTreeWidget : public QTreeWidget { @@ -55,6 +56,9 @@ public: protected: void keyPressEvent(QKeyEvent *event) override; +Q_SIGNALS: + void selectedCommentsChanged(std::vector> comments); + private Q_SLOTS: void commentsChanged(); void ContextMenuRequested(QPoint point); @@ -65,27 +69,29 @@ private: TraceNavigator *navigator; void printComments(); QAction *deleteComment; - QAction *changeCommentText; + QAction *editCommentText; class CommentTreeItem : public QTreeWidgetItem { private: - Comment comment; + const std::shared_ptr comment; public: - CommentTreeItem(QTreeWidget *parent, const Comment &comment); + CommentTreeItem(QTreeWidget *parent, const std::shared_ptr comment); QString Text() { - return comment.Text(); + return comment->Text(); } traceTime Time() { - return comment.Time(); + return comment->Time(); } - void changeText(QString newText) + const std::shared_ptr getComment() { - comment.changeText(newText); + return comment; } }; + + CommentTreeItem *getTreeItemFromTime(traceTime time); }; #endif // COMMENTTREEWIDGET_H diff --git a/DRAMSys/traceAnalyzer/presentation/tracenavigator.cpp b/DRAMSys/traceAnalyzer/presentation/tracenavigator.cpp index 13b0526c..0758cd01 100644 --- a/DRAMSys/traceAnalyzer/presentation/tracenavigator.cpp +++ b/DRAMSys/traceAnalyzer/presentation/tracenavigator.cpp @@ -39,6 +39,8 @@ #include "tracenavigator.h" #include "util/traceplotlinecache.h" #include "vector" +#include +#include using namespace std; @@ -85,7 +87,7 @@ void TraceNavigator::navigateToTransaction(ID id) */ void TraceNavigator::insertComment(const Comment &comment) { - comments.emplace(comment.Time(), comment); + comments.emplace(comment.Time(), std::make_shared(comment)); changesToCommitExist = true; Q_EMIT commentsChanged(); } @@ -93,6 +95,7 @@ void TraceNavigator::insertComment(const Comment &comment) void TraceNavigator::removeCommentAtTime(traceTime time) { auto found = comments.find(time); + if (found != comments.end()) { comments.erase(found); changesToCommitExist = true; @@ -100,7 +103,60 @@ void TraceNavigator::removeCommentAtTime(traceTime time) } } +void TraceNavigator::editCommentAtTime(traceTime time) +{ + auto found = comments.find(time); + if (found != comments.end()) { + comments.erase(found); + auto comment = found->second; + + bool ok; + QString newText = QInputDialog::getText(nullptr, QString("Edit comment text"), + QString("Edit comment text"), QLineEdit::Normal, + comment->Text(), &ok); + + if (ok) + { + removeCommentAtTime(time); + insertComment({comment->Time(), newText}); + changesToCommitExist = true; + Q_EMIT commentsChanged(); + } + } +} + +void TraceNavigator::addSelectedComments(const std::vector> + &comments) +{ + for (auto &comment : comments) + selectedComments.push_back(comment); + + Q_EMIT selectedCommentsChanged(); +} + +void TraceNavigator::clearSelectedComments() +{ + selectedComments.clear(); + Q_EMIT selectedCommentsChanged(); +} + +void TraceNavigator::updateCommentSelection(std::vector> comments) +{ + selectedComments.clear(); + addSelectedComments(comments); +} + +bool TraceNavigator::commentIsSelected(const std::shared_ptr comment) const +{ + for (auto &selectedComment : selectedComments) + { + if (comment == selectedComment) + return true; + } + + return false; +} /* DB * @@ -110,7 +166,7 @@ void TraceNavigator::commitChangesToDB() { vector commentsToInsert; for (const auto &pair : comments) { - commentsToInsert.push_back(pair.second); + commentsToInsert.push_back(*pair.second.get()); } traceFile.updateComments(commentsToInsert); @@ -120,7 +176,7 @@ void TraceNavigator::commitChangesToDB() void TraceNavigator::getCommentsFromDB() { for (const Comment &comment : traceFile.getComments()) { - comments.emplace(comment.Time(), comment); + comments.emplace(comment.Time(), std::make_shared(comment)); } } diff --git a/DRAMSys/traceAnalyzer/presentation/tracenavigator.h b/DRAMSys/traceAnalyzer/presentation/tracenavigator.h index 28bc8af7..00c76294 100644 --- a/DRAMSys/traceAnalyzer/presentation/tracenavigator.h +++ b/DRAMSys/traceAnalyzer/presentation/tracenavigator.h @@ -56,7 +56,7 @@ class TracePlotLineCache; class TraceNavigator : public QObject { Q_OBJECT - using CommentMap = std::map; + using CommentMap = std::map>; public: TraceNavigator(QString path, QObject *parent = 0); @@ -117,6 +117,16 @@ public: return comments; } void removeCommentAtTime(traceTime time); + void editCommentAtTime(traceTime time); + void addSelectedComments(const std::vector> + &comments); + void clearSelectedComments(); + const std::vector> &SelectedComments() + { + return selectedComments; + } + + bool commentIsSelected(const std::shared_ptr comment) const; void commitChangesToDB(); void refreshData(); @@ -127,8 +137,12 @@ public: Q_SIGNALS: void currentTraceTimeChanged(); void selectedTransactionsChanged(); + void selectedCommentsChanged(); void commentsChanged(); +public Q_SLOTS: + void updateCommentSelection(std::vector> comments); + private: TraceDB traceFile; @@ -136,6 +150,7 @@ private: //components drawing the tracefile center around that time traceTime currentTraceTime = 0; std::vector> selectedTransactions; + std::vector> selectedComments; CommentMap comments; void getCommentsFromDB(); bool changesToCommitExist; diff --git a/DRAMSys/traceAnalyzer/presentation/traceplot.cpp b/DRAMSys/traceAnalyzer/presentation/traceplot.cpp index fef381a1..64fbf582 100644 --- a/DRAMSys/traceAnalyzer/presentation/traceplot.cpp +++ b/DRAMSys/traceAnalyzer/presentation/traceplot.cpp @@ -216,6 +216,8 @@ void TracePlot::connectNavigatorQ_SIGNALS() SLOT(currentTraceTimeChanged())); QObject::connect(navigator, SIGNAL(selectedTransactionsChanged()), this, SLOT(selectedTransactionsChanged())); + QObject::connect(navigator, SIGNAL(selectedCommentsChanged()), this, + SLOT(commentsChanged())); QObject::connect(navigator, SIGNAL(commentsChanged()), this, SLOT(commentsChanged())); } @@ -344,15 +346,17 @@ Timespan TracePlot::GetCurrentTimespan() void TracePlot::getAndDrawComments() { for (const auto &pair : navigator->getComments()) { - const Comment &comment = pair.second; - QwtPlotMarker *maker = new QwtPlotMarker(); - maker->setLabel(comment.Text()); - maker->setLabelOrientation(Qt::Vertical); - maker->setLabelAlignment(Qt::AlignLeft | Qt::AlignBottom); - maker->setXValue(static_cast(comment.Time())); - maker->setLineStyle(QwtPlotMarker::LineStyle::VLine); - maker->setLinePen(QColor(Qt::blue), 2); - maker->attach(this); + const std::shared_ptr comment = pair.second; + bool highlight = navigator->commentIsSelected(comment); + + QwtPlotMarker *marker = new QwtPlotMarker(); + marker->setLabel(comment->Text()); + marker->setLabelOrientation(Qt::Vertical); + marker->setLabelAlignment(Qt::AlignLeft | Qt::AlignBottom); + marker->setXValue(static_cast(comment->Time())); + marker->setLineStyle(QwtPlotMarker::LineStyle::VLine); + marker->setLinePen(QColor(highlight ? Qt::red : Qt::blue), 2); + marker->attach(this); } } @@ -646,9 +650,13 @@ bool TracePlot::eventFilter(QObject *object, QEvent *event) mouseDownData.mouseIsDownForDragging = true; canvas()->setCursor(Qt::ClosedHandCursor); SelectTransaction(mouseEvent->x(), mouseEvent->y()); + SelectComment(mouseEvent->x()); } return true; } else if (mouseEvent->button() == Qt::RightButton) { + // Also select comments to make it more obvious. + SelectComment(mouseEvent->x()); + openContextMenu(this->canvas()->mapToGlobal(mouseEvent->pos()), mouseEvent->pos()); return true; @@ -704,7 +712,7 @@ bool TracePlot::eventFilter(QObject *object, QEvent *event) return false; } -void TracePlot::SelectTransaction(int x, int y) +void TracePlot::SelectTransaction(int x, int y) const { double yVal = invTransform(yLeft, y); traceTime time = invTransform(xBottom, x); @@ -717,10 +725,61 @@ void TracePlot::SelectTransaction(int x, int y) } } +void TracePlot::SelectComment(int x) const +{ + traceTime time = invTransform(xBottom, x); + + auto selectedComments = hoveredComments(time); + + if (selectedComments.size() > 0) { + if (!keyPressData.ctrlPressed) + navigator->clearSelectedComments(); + + navigator->addSelectedComments(selectedComments); + } else { + navigator->clearSelectedComments(); + } +} + +const std::vector> TracePlot::hoveredComments(traceTime time) const +{ + std::vector> hoveredComments; + + for (const auto &commentPair : navigator->getComments()) + { + auto comment = commentPair.second; + if (comment->isSelected(time, navigator->GeneralTraceInfo().clkPeriod)) + hoveredComments.push_back(comment); + } + + return hoveredComments; +} + void TracePlot::openContextMenu(const QPoint &pos, const QPoint &mouseDown) { contextMenuMouseDown = mouseDown; - contextMenu->exec(pos); + + traceTime time = invTransform(xBottom, mouseDown.x()); + auto comments = hoveredComments(time); + + if (comments.size() == 0) + contextMenu->exec(pos); + else + { + QMenu commentContextMenu(this); + QAction editCommentText("Edit comment text", this); + QAction deleteComment("Delete comment", this); + + auto commentTime = comments[0]->Time(); + connect(&editCommentText, &QAction::triggered, this, [=](){ + navigator->editCommentAtTime(commentTime); + }); + + connect(&deleteComment, &QAction::triggered, this, [=](){ + navigator->removeCommentAtTime(commentTime); + }); + + commentContextMenu.addActions({&editCommentText, &deleteComment}); + commentContextMenu.exec(pos); + } } - - diff --git a/DRAMSys/traceAnalyzer/presentation/traceplot.h b/DRAMSys/traceAnalyzer/presentation/traceplot.h index 2676988d..10dd534d 100644 --- a/DRAMSys/traceAnalyzer/presentation/traceplot.h +++ b/DRAMSys/traceAnalyzer/presentation/traceplot.h @@ -186,7 +186,9 @@ private: void openContextMenu(const QPoint &pos, const QPoint &mouseDown); QPoint contextMenuMouseDown; - void SelectTransaction(int x, int y); + void SelectTransaction(int x, int y) const; + void SelectComment(int x) const; + const std::vector> hoveredComments(traceTime time) const; void keyPressEvent(QKeyEvent *keyPressedEvent); void keyReleaseEvent(QKeyEvent *keyReleasedEvent); diff --git a/DRAMSys/traceAnalyzer/presentation/tracescroller.cpp b/DRAMSys/traceAnalyzer/presentation/tracescroller.cpp index ae9906a1..91b8b3a2 100644 --- a/DRAMSys/traceAnalyzer/presentation/tracescroller.cpp +++ b/DRAMSys/traceAnalyzer/presentation/tracescroller.cpp @@ -121,6 +121,8 @@ void TraceScroller::connectNavigatorQ_SIGNALS() SLOT(currentTraceTimeChanged())); QObject::connect(navigator, SIGNAL(commentsChanged()), this, SLOT(commentsChanged())); + QObject::connect(navigator, SIGNAL(selectedCommentsChanged()), this, + SLOT(commentsChanged())); QObject::connect(navigator, SIGNAL(selectedTransactionsChanged()), this, SLOT(selectedTransactionsChanged())); } @@ -148,11 +150,13 @@ Timespan TraceScroller::GetCurrentTimespan() void TraceScroller::getAndDrawComments() { for (const auto &pair : navigator->getComments()) { - const Comment &comment = pair.second; + const std::shared_ptr comment = pair.second; + bool highlight = navigator->commentIsSelected(comment); + QwtPlotMarker *maker = new QwtPlotMarker(); - maker->setXValue(static_cast(comment.Time())); + maker->setXValue(static_cast(comment->Time())); maker->setLineStyle(QwtPlotMarker::LineStyle::VLine); - maker->setLinePen(QColor(Qt::blue), 2); + maker->setLinePen(QColor(highlight ? Qt::red : Qt::blue), 2); maker->attach(this); } } diff --git a/DRAMSys/traceAnalyzer/tracefiletab.cpp b/DRAMSys/traceAnalyzer/tracefiletab.cpp index 36c95509..1fe452d0 100644 --- a/DRAMSys/traceAnalyzer/tracefiletab.cpp +++ b/DRAMSys/traceAnalyzer/tracefiletab.cpp @@ -89,6 +89,9 @@ TraceFileTab::TraceFileTab(QWidget *parent, const QString &path) : ui->memSpecView->setModel(memSpecModel); ui->memSpecView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + connect(ui->commentTree, &CommentTreeWidget::selectedCommentsChanged, + navigator, &TraceNavigator::updateCommentSelection); + tracefileChanged(); }