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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user