Add functionality to select comments and directly delete/edit them

It's now possible to select comments directly in the traceplot instead
of selecting them in the CommentTreeWidget.
Selections from the TreeWidget are synchronized to the plot but not vice
versa.

It would generally be the better to introduce a Model/View based
approach instead of trying to synchronize selections and QActions.
This commit is contained in:
2021-08-30 14:32:05 +02:00
parent ccab0b3cfb
commit f6b987f777
9 changed files with 207 additions and 52 deletions

View File

@@ -59,6 +59,16 @@ public:
{
text = newText;
}
bool isSelected(traceTime mouseTime, traceTime clk)
{
// Increase selectable area of comments
traceTime offset = static_cast<traceTime>(0.35 * clk);
if (time >= (mouseTime - offset) && time <= (mouseTime + offset))
return true;
return false;
}
};
#endif // COMMENT_H

View File

@@ -41,12 +41,10 @@
#include <iostream>
#include <QMenu>
#include <QKeyEvent>
#include <QInputDialog>
#include <QLineEdit>
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<CommentTreeItem *>(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<std::shared_ptr<Comment>> selectedComments;
for (auto &item : selectedItems())
{
CommentTreeItem *commentItem = static_cast<CommentTreeItem *>(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> 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> 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);
}

View File

@@ -43,6 +43,7 @@
#include <QAction>
#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<std::shared_ptr<Comment>> 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> comment;
public:
CommentTreeItem(QTreeWidget *parent, const Comment &comment);
CommentTreeItem(QTreeWidget *parent, const std::shared_ptr<Comment> 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<Comment> getComment()
{
comment.changeText(newText);
return comment;
}
};
CommentTreeItem *getTreeItemFromTime(traceTime time);
};
#endif // COMMENTTREEWIDGET_H

View File

@@ -39,6 +39,8 @@
#include "tracenavigator.h"
#include "util/traceplotlinecache.h"
#include "vector"
#include <QInputDialog>
#include <QLineEdit>
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>(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<std::shared_ptr<Comment>>
&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<std::shared_ptr<Comment>> comments)
{
selectedComments.clear();
addSelectedComments(comments);
}
bool TraceNavigator::commentIsSelected(const std::shared_ptr<Comment> comment) const
{
for (auto &selectedComment : selectedComments)
{
if (comment == selectedComment)
return true;
}
return false;
}
/* DB
*
@@ -110,7 +166,7 @@ void TraceNavigator::commitChangesToDB()
{
vector<Comment> 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>(comment));
}
}

View File

@@ -56,7 +56,7 @@ class TracePlotLineCache;
class TraceNavigator : public QObject
{
Q_OBJECT
using CommentMap = std::map<traceTime, Comment>;
using CommentMap = std::map<traceTime, std::shared_ptr<Comment>>;
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<std::shared_ptr<Comment>>
&comments);
void clearSelectedComments();
const std::vector<std::shared_ptr<Comment>> &SelectedComments()
{
return selectedComments;
}
bool commentIsSelected(const std::shared_ptr<Comment> 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<std::shared_ptr<Comment>> comments);
private:
TraceDB traceFile;
@@ -136,6 +150,7 @@ private:
//components drawing the tracefile center around that time
traceTime currentTraceTime = 0;
std::vector<std::shared_ptr<Transaction>> selectedTransactions;
std::vector<std::shared_ptr<Comment>> selectedComments;
CommentMap comments;
void getCommentsFromDB();
bool changesToCommitExist;

View File

@@ -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<double>(comment.Time()));
maker->setLineStyle(QwtPlotMarker::LineStyle::VLine);
maker->setLinePen(QColor(Qt::blue), 2);
maker->attach(this);
const std::shared_ptr<Comment> 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<double>(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<std::shared_ptr<Comment>> TracePlot::hoveredComments(traceTime time) const
{
std::vector<std::shared_ptr<Comment>> 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);
}
}

View File

@@ -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<std::shared_ptr<Comment>> hoveredComments(traceTime time) const;
void keyPressEvent(QKeyEvent *keyPressedEvent);
void keyReleaseEvent(QKeyEvent *keyReleasedEvent);

View File

@@ -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> comment = pair.second;
bool highlight = navigator->commentIsSelected(comment);
QwtPlotMarker *maker = new QwtPlotMarker();
maker->setXValue(static_cast<double>(comment.Time()));
maker->setXValue(static_cast<double>(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);
}
}

View File

@@ -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();
}