diff --git a/DRAMSys/traceAnalyzer/CMakeLists.txt b/DRAMSys/traceAnalyzer/CMakeLists.txt index ae70b48c..96594a93 100644 --- a/DRAMSys/traceAnalyzer/CMakeLists.txt +++ b/DRAMSys/traceAnalyzer/CMakeLists.txt @@ -75,7 +75,6 @@ add_executable(TraceAnalyzer presentation/tracescroller.cpp traceanalyzer.cpp presentation/transactiontreewidget.cpp - presentation/commenttreewidget.cpp presentation/util/clkgrid.cpp queryeditor.cpp presentation/selectedtransactiontreewidget.cpp @@ -97,6 +96,7 @@ add_executable(TraceAnalyzer presentation/util/customlabelscaledraw.cpp presentation/traceselector.cpp businessObjects/configmodels.cpp + businessObjects/commentmodel.cpp selectmetrics.ui preferences.ui diff --git a/DRAMSys/traceAnalyzer/businessObjects/comment.h b/DRAMSys/traceAnalyzer/businessObjects/comment.h deleted file mode 100644 index c52753f8..00000000 --- a/DRAMSys/traceAnalyzer/businessObjects/comment.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2015, Technische Universität Kaiserslautern - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Authors: - * Janik Schlemminger - * Robert Gernhardt - * Matthias Jung - * Derek Christ - */ - -#ifndef COMMENT_H -#define COMMENT_H -#include -#include "timespan.h" - -class Comment -{ - traceTime time; - QString text; -public: - Comment(traceTime time, QString text): time(time), text(text) {} - traceTime Time() const - { - return time; - } - const QString &Text() const - { - return text; - } - void changeText(QString newText) - { - text = newText; - } -}; - -#endif // COMMENT_H diff --git a/DRAMSys/traceAnalyzer/businessObjects/commentmodel.cpp b/DRAMSys/traceAnalyzer/businessObjects/commentmodel.cpp new file mode 100644 index 00000000..c6cdad28 --- /dev/null +++ b/DRAMSys/traceAnalyzer/businessObjects/commentmodel.cpp @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2021, Technische Universität Kaiserslautern + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: + * Derek Christ + */ + +#include "commentmodel.h" + +#include +#include +#include + +CommentModel::CommentModel(QObject *parent) : QAbstractTableModel(parent), + gotoAction(new QAction("Goto comment", this)), + editAction(new QAction("Edit comment", this)), + deleteAction(new QAction("Delete comment", this)), + selectAllAction(new QAction("Select all comments", this)), + deselectAllAction(new QAction("Deselect all comments", this)), + internalSelectionModel(new QItemSelectionModel(this, this)) +{ + setUpActions(); +} + +void CommentModel::setUpActions() +{ + QObject::connect(gotoAction, &QAction::triggered, this, [=](){ + const QModelIndexList indexes = internalSelectionModel->selectedRows(); + for (const QModelIndex ¤tIndex : indexes) + { + emit gotoCommentTriggered(currentIndex); + } + }); + + QObject::connect(editAction, &QAction::triggered, this, [=](){ + const QModelIndexList indexes = internalSelectionModel->selectedRows(); + for (const QModelIndex ¤tIndex : indexes) + emit editTriggered(index(currentIndex.row(), static_cast(Column::Comment))); + }); + + QObject::connect(deleteAction, &QAction::triggered, this, [=](){ + const QModelIndexList indexes = internalSelectionModel->selectedRows(); + for (const QModelIndex ¤tIndex : indexes) + removeComment(currentIndex); + }); + + QObject::connect(selectAllAction, &QAction::triggered, this, [=](){ + QModelIndex topLeft = index(0, 0); + QModelIndex bottomRight = index(rowCount() - 1, columnCount() - 1); + internalSelectionModel->select(QItemSelection(topLeft, bottomRight), + QItemSelectionModel::Select | QItemSelectionModel::Rows); + }); + + QObject::connect(deselectAllAction, &QAction::triggered, + internalSelectionModel, &QItemSelectionModel::clearSelection); +} + +int CommentModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return comments.size(); +} + +int CommentModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return static_cast(Column::COLUMNCOUNT); +} + +QVariant CommentModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + // Qt::UserRole is used to get the raw time without pretty formatting. + if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::UserRole) + return QVariant(); + + const Comment &comment = comments.at(index.row()); + + if (role == Qt::UserRole && static_cast(index.column()) == Column::Time) + return QVariant(comment.time); + + switch (static_cast(index.column())) { + case Column::Time: + return QVariant(prettyFormatTime(comment.time)); + case Column::Comment: + return QVariant(comment.text); + default: + break; + } + + return QVariant(); +} + +bool CommentModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid()) + return false; + + if (role != Qt::EditRole) + return false; + + if (static_cast(index.column()) != Column::Comment) + return false; + + QString newText = value.toString(); + + Comment &comment = comments.at(index.row()); + comment.text = newText; + + emit dataChanged(index, index); + return true; +} + +QVariant CommentModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + if (orientation == Qt::Horizontal) + { + switch (static_cast(section)) { + case Column::Time: + return "Time"; + case Column::Comment: + return "Comment"; + default: + break; + } + } + + return QVariant(); +} + +Qt::ItemFlags CommentModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags flags = QAbstractItemModel::flags(index); + + if (index.isValid() && index.column() == static_cast(Column::Comment)) + flags |= Qt::ItemIsEditable; + + return flags; +} + +void CommentModel::openContextMenu() +{ + if (!internalSelectionModel->hasSelection()) + return; + + QMenu *menu = new QMenu(); + menu->addActions({gotoAction, editAction, deleteAction}); + menu->addSeparator(); + menu->addActions({selectAllAction, deselectAllAction}); + + QObject::connect(menu, &QMenu::aboutToHide, [=]() { + menu->deleteLater(); + }); + + menu->popup(QCursor::pos()); +} + +QItemSelectionModel *CommentModel::selectionModel() const +{ + return internalSelectionModel; +} + +void CommentModel::addComment(traceTime time) +{ + auto commentIt = std::find_if(comments.rbegin(), comments.rend(), [time](const Comment &comment){ + return comment.time <= time; + }); + + int insertIndex = std::distance(comments.begin(), commentIt.base()); + + beginInsertRows(QModelIndex(), insertIndex, insertIndex); + comments.insert(comments.begin() + insertIndex, {time, "Enter comment text..."}); + endInsertRows(); + + internalSelectionModel->setCurrentIndex(index(insertIndex, 0), + QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + + emit editTriggered(index(insertIndex, 1)); +} + +void CommentModel::addComment(traceTime time, QString text) +{ + auto commentIt = std::find_if(comments.rbegin(), comments.rend(), [time](const Comment &comment){ + return comment.time <= time; + }); + + int insertIndex = std::distance(comments.begin(), commentIt.base()); + + beginInsertRows(QModelIndex(), insertIndex, insertIndex); + comments.insert(comments.begin() + insertIndex, {time, text}); + endInsertRows(); +} + +void CommentModel::removeComment(traceTime time) +{ + auto commentIt = std::find_if(comments.begin(), comments.end(), [time](const Comment &comment){ + return comment.time == time; + }); + + if (commentIt == comments.end()) + return; + + int removeIndex = std::distance(comments.begin(), commentIt); + + beginRemoveRows(QModelIndex(), removeIndex, removeIndex); + comments.erase(commentIt); + endRemoveRows(); +} + +void CommentModel::removeComment(const QModelIndex &index) +{ + beginRemoveRows(QModelIndex(), index.row(), index.row()); + comments.erase(comments.begin() + index.row()); + endRemoveRows(); +} + +const std::vector &CommentModel::getComments() const +{ + return comments; +} + +traceTime CommentModel::getTimeFromIndex(const QModelIndex &index) const +{ + Q_ASSERT(comments.size() > index.row()); + + return comments[index.row()].time; +} + +QModelIndex CommentModel::hoveredComment(Timespan timespan) const +{ + auto commentIt = std::find_if(comments.begin(), comments.end(), [timespan](const Comment &comment){ + return timespan.Begin() < comment.time && comment.time < timespan.End(); + }); + + if (commentIt == comments.end()) + return QModelIndex(); + + int commentIndex = std::distance(comments.begin(), commentIt); + + return index(commentIndex, 0); +} + +bool CommentModel::eventFilter(QObject *object, QEvent *event) +{ + Q_UNUSED(object) + + if (event->type() == QEvent::KeyPress) + { + QKeyEvent *keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Delete) + { + const QModelIndexList indexes = internalSelectionModel->selectedRows(); + for (const QModelIndex ¤tIndex : indexes) + { + removeComment(currentIndex); + } + } + } + + return false; +} + +void CommentModel::rowDoubleClicked(const QModelIndex &index) +{ + if (!index.isValid()) + return; + + if (static_cast(index.column()) == Column::Time) + Q_EMIT gotoCommentTriggered(index); +} diff --git a/DRAMSys/traceAnalyzer/businessObjects/commentmodel.h b/DRAMSys/traceAnalyzer/businessObjects/commentmodel.h new file mode 100644 index 00000000..a9e165a6 --- /dev/null +++ b/DRAMSys/traceAnalyzer/businessObjects/commentmodel.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2021, Technische Universität Kaiserslautern + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: + * Derek Christ + */ + +#ifndef COMMENTMODEL_H +#define COMMENTMODEL_H + +#include "tracetime.h" +#include "timespan.h" + +#include +#include + +class QAction; +class QItemSelectionModel; + +class CommentModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + explicit CommentModel(QObject *parent = nullptr); + + struct Comment { + traceTime time; + QString text; + }; + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + Qt::ItemFlags flags(const QModelIndex &index) const override; + + enum class Column { + Time = 0, + Comment, + COLUMNCOUNT + }; + + void openContextMenu(); + + QItemSelectionModel *selectionModel() const; + + void addComment(traceTime time); + void addComment(traceTime time, QString text); + + void removeComment(traceTime time); + void removeComment(const QModelIndex &index); + + const std::vector &getComments() const; + + traceTime getTimeFromIndex(const QModelIndex &index) const; + + QModelIndex hoveredComment(Timespan timespan) const; + +public Q_SLOTS: + void rowDoubleClicked(const QModelIndex &index); + +protected: + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + + /** + * The eventFilter is used to delete comments using the delete key. + */ + bool eventFilter(QObject *object, QEvent *event) override; + +Q_SIGNALS: + void editTriggered(const QModelIndex &index); + void gotoCommentTriggered(const QModelIndex &index); + +private: + void setUpActions(); + + std::vector comments; + + QAction *gotoAction; + QAction *editAction; + QAction *deleteAction; + QAction *selectAllAction; + QAction *deselectAllAction; + + QItemSelectionModel *internalSelectionModel; +}; + +#endif // COMMENTMODEL_H diff --git a/DRAMSys/traceAnalyzer/data/tracedb.cpp b/DRAMSys/traceAnalyzer/data/tracedb.cpp index a7061a70..ff37a88f 100644 --- a/DRAMSys/traceAnalyzer/data/tracedb.cpp +++ b/DRAMSys/traceAnalyzer/data/tracedb.cpp @@ -44,7 +44,6 @@ #include #include #include "data/tracedb.h" -#include "businessObjects/comment.h" #include "businessObjects/phases/phasefactory.h" @@ -87,7 +86,7 @@ void TraceDB::prepareQueries() selectDebugMessagesByTimespanWithLimit.prepare("SELECT time, Message FROM DebugMessages WHERE :begin <= time AND time <= :end LIMIT :limit"); } -void TraceDB::updateComments(vector comments) +void TraceDB::updateComments(const std::vector &comments) { QSqlQuery query(database); @@ -95,9 +94,10 @@ void TraceDB::updateComments(vector comments) executeQuery(query); query.prepare("insert into Comments values(:time,:text)"); - for (const Comment &comment : comments) { - query.bindValue(":time", comment.Time()); - query.bindValue(":text", comment.Text()); + for (const auto &comment : comments) + { + query.bindValue(":time", comment.time); + query.bindValue(":text", comment.text); executeQuery(query); } } @@ -341,7 +341,7 @@ unsigned int TraceDB::getNumberOfPhases() return query.value(0).toInt(); } -vector TraceDB::getComments() +vector TraceDB::getComments() { QSqlQuery query(database); query.prepare("SELECT Time,Text From Comments"); @@ -350,7 +350,7 @@ vector TraceDB::getComments() } -vector TraceDB::getDebugMessagesInTimespan(const Timespan &span) +vector TraceDB::getDebugMessagesInTimespan(const Timespan &span) { selectDebugMessagesByTimespan.bindValue(":begin", span.Begin()); selectDebugMessagesByTimespan.bindValue(":end", span.End()); @@ -359,7 +359,7 @@ vector TraceDB::getDebugMessagesInTimespan(const Timespan &span) return parseCommentsFromQuery(selectDebugMessagesByTimespan); } -vector TraceDB::getDebugMessagesInTimespan(const Timespan &span, +vector TraceDB::getDebugMessagesInTimespan(const Timespan &span, unsigned int limit = 50) { selectDebugMessagesByTimespanWithLimit.bindValue(":begin", span.Begin()); @@ -432,12 +432,12 @@ vector> TraceDB::parseTransactionsFromQuery( return result; } -vector TraceDB::parseCommentsFromQuery(QSqlQuery &query) +vector TraceDB::parseCommentsFromQuery(QSqlQuery &query) { - vector result; + vector result; while (query.next()) { - result.push_back(Comment(query.value(0).toLongLong(), - query.value(1).toString())); + result.push_back(CommentModel::Comment{query.value(0).toLongLong(), + query.value(1).toString()}); } return result; } diff --git a/DRAMSys/traceAnalyzer/data/tracedb.h b/DRAMSys/traceAnalyzer/data/tracedb.h index 5df215b1..511be6b0 100644 --- a/DRAMSys/traceAnalyzer/data/tracedb.h +++ b/DRAMSys/traceAnalyzer/data/tracedb.h @@ -50,7 +50,7 @@ #include "businessObjects/generalinfo.h" #include "businessObjects/commandlengths.h" #include "businessObjects/phases/phasefactory.h" -#include "businessObjects/comment.h" +#include "businessObjects/commentmodel.h" #include "QueryTexts.h" /* TraceDB handles the connection to a SQLLite database containing trace data. @@ -68,7 +68,7 @@ public: return pathToDB; } - void updateComments(std::vector comments); + void updateComments(const std::vector &comments); void updateFileDescription(const QString &description); void refreshData(); @@ -98,9 +98,9 @@ public: std::shared_ptr getTransactionByID(ID id); ID getTransactionIDFromPhaseID(ID phaseID); - std::vector getComments(); - std::vector getDebugMessagesInTimespan(const Timespan &span); - std::vector getDebugMessagesInTimespan(const Timespan &span, + std::vector getComments(); + std::vector getDebugMessagesInTimespan(const Timespan &span); + std::vector getDebugMessagesInTimespan(const Timespan &span, unsigned int limit); QSqlDatabase getDatabase() const; @@ -125,7 +125,7 @@ private: std::shared_ptr parseTransactionFromQuery(QSqlQuery &query); std::vector> parseTransactionsFromQuery( QSqlQuery &query); - std::vector parseCommentsFromQuery(QSqlQuery &query); + std::vector parseCommentsFromQuery(QSqlQuery &query); void executeScriptFile(QString fileName); void dropAndCreateTables(); diff --git a/DRAMSys/traceAnalyzer/presentation/commenttreewidget.cpp b/DRAMSys/traceAnalyzer/presentation/commenttreewidget.cpp deleted file mode 100644 index 519e04e4..00000000 --- a/DRAMSys/traceAnalyzer/presentation/commenttreewidget.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2015, Technische Universität Kaiserslautern - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Authors: - * Janik Schlemminger - * Robert Gernhardt - * Matthias Jung - * Derek Christ - */ - -#include "commenttreewidget.h" -#include -#include -#include -#include -#include -#include - -CommentTreeWidget::CommentTreeWidget(QWidget *parent) : QTreeWidget(parent), - deleteComment(new QAction("Delete comment", this)), - changeCommentText(new QAction("Change comment text", this)) -{ - connect(deleteComment, &QAction::triggered, this, [this](){ - for (QTreeWidgetItem *item : selectedItems()) - { - CommentTreeItem *commentItem = static_cast(item); - navigator->removeCommentAtTime(commentItem->Time()); - } - }); - - connect(changeCommentText, &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, - QString(), &ok); - - if (ok) - { - deleteComment->trigger(); - navigator->insertComment({commentItem->Time(), newText}); - } - - commentsChanged(); - } - }); -} - -void CommentTreeWidget::init(TraceNavigator *navigator) -{ - Q_ASSERT(isInitialized == false); - isInitialized = true; - this->navigator = navigator; - setColumnCount(2); - setHeaderLabels(QStringList({"Time", "Comment"})); - QObject::connect(navigator, SIGNAL(commentsChanged()), this, - SLOT(commentsChanged())); - QObject::connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, - SLOT(itemDoubleClicked(QTreeWidgetItem *, int))); - QObject::connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, - SLOT(ContextMenuRequested(QPoint))); - setContextMenuPolicy(Qt::CustomContextMenu); - printComments(); -} - -void CommentTreeWidget::commentsChanged() -{ - clear(); - printComments(); -} - -void CommentTreeWidget::printComments() -{ - for (const auto &pair : navigator->getComments()) { - const Comment &comment = pair.second; - QTreeWidgetItem *item = new CommentTreeItem(this, comment); - addTopLevelItem(item); - } - resizeColumnToContents(0); -} - -void CommentTreeWidget::itemDoubleClicked(QTreeWidgetItem *item, int /*column*/) -{ - CommentTreeItem *commentItem = static_cast(item); - navigator->navigateToTime(commentItem->Time()); -} - -void CommentTreeWidget::ContextMenuRequested(QPoint point) -{ - QMenu contextMenu(this); - contextMenu.addActions({deleteComment, changeCommentText}); - contextMenu.exec(mapToGlobal(point)); -} - - -CommentTreeWidget::CommentTreeItem::CommentTreeItem(QTreeWidget *parent, - const Comment &comment): QTreeWidgetItem(parent), comment(comment) -{ - this->setText(0, prettyFormatTime(comment.Time())); - this->setText(1, comment.Text()); -} - -void CommentTreeWidget::keyPressEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_Delete) - deleteComment->trigger(); - else if (event->key() == Qt::Key_F2) - changeCommentText->trigger(); - - QTreeWidget::keyPressEvent(event); -} diff --git a/DRAMSys/traceAnalyzer/presentation/commenttreewidget.h b/DRAMSys/traceAnalyzer/presentation/commenttreewidget.h deleted file mode 100644 index 7429f75e..00000000 --- a/DRAMSys/traceAnalyzer/presentation/commenttreewidget.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2015, Technische Universität Kaiserslautern - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Authors: - * Janik Schlemminger - * Robert Gernhardt - * Matthias Jung - * Derek Christ - */ - -#ifndef COMMENTTREEWIDGET_H -#define COMMENTTREEWIDGET_H -#include -#include -#include -#include "businessObjects/comment.h" -#include "tracenavigator.h" - -class CommentTreeWidget : public QTreeWidget -{ - Q_OBJECT - -public: - CommentTreeWidget(QWidget *parent = nullptr); - void init(TraceNavigator *navigator); - -protected: - void keyPressEvent(QKeyEvent *event) override; - -private Q_SLOTS: - void commentsChanged(); - void ContextMenuRequested(QPoint point); - void itemDoubleClicked(QTreeWidgetItem *item, int column); - -private: - bool isInitialized = false; - TraceNavigator *navigator; - void printComments(); - QAction *deleteComment; - QAction *changeCommentText; - - class CommentTreeItem : public QTreeWidgetItem - { - private: - Comment comment; - public: - CommentTreeItem(QTreeWidget *parent, const Comment &comment); - QString Text() - { - return comment.Text(); - } - traceTime Time() - { - return comment.Time(); - } - void changeText(QString newText) - { - comment.changeText(newText); - } - }; -}; - -#endif // COMMENTTREEWIDGET_H diff --git a/DRAMSys/traceAnalyzer/presentation/debugmessagetreewidget.cpp b/DRAMSys/traceAnalyzer/presentation/debugmessagetreewidget.cpp index df4bdee1..13f1cc7c 100644 --- a/DRAMSys/traceAnalyzer/presentation/debugmessagetreewidget.cpp +++ b/DRAMSys/traceAnalyzer/presentation/debugmessagetreewidget.cpp @@ -84,19 +84,19 @@ void DebugMessageTreeWidget::currentTraceTimeChanged() traceplot->GetCurrentTimespan())); } -void DebugMessageTreeWidget::showDebugMessages(const vector &comments) +void DebugMessageTreeWidget::showDebugMessages(const vector &comments) { clear(); if (comments.empty()) return; traceTime currentTime = -1; - for (Comment comment : comments) { - if (currentTime != comment.Time()) { - addTopLevelItem(new QTreeWidgetItem({prettyFormatTime(comment.Time()), formatDebugMessage(comment.Text())})); - currentTime = comment.Time(); + for (const auto &comment : comments) { + if (currentTime != comment.time) { + addTopLevelItem(new QTreeWidgetItem({prettyFormatTime(comment.time), formatDebugMessage(comment.text)})); + currentTime = comment.time; } else { - addTopLevelItem(new QTreeWidgetItem({"", formatDebugMessage(comment.Text())})); + addTopLevelItem(new QTreeWidgetItem({"", formatDebugMessage(comment.text)})); } } diff --git a/DRAMSys/traceAnalyzer/presentation/debugmessagetreewidget.h b/DRAMSys/traceAnalyzer/presentation/debugmessagetreewidget.h index 906188c6..225a5bf0 100644 --- a/DRAMSys/traceAnalyzer/presentation/debugmessagetreewidget.h +++ b/DRAMSys/traceAnalyzer/presentation/debugmessagetreewidget.h @@ -42,7 +42,7 @@ #include #include #include -#include "businessObjects/comment.h" +#include "businessObjects/commentmodel.h" #include "tracenavigator.h" #include "traceplot.h" @@ -56,7 +56,7 @@ public: hexAdressMatcher(QString("0x[0-9,a-f]+")) {} void init(TraceNavigator *navigator, TracePlot *traceplot); - void showDebugMessages(const std::vector &comments); + void showDebugMessages(const std::vector &comments); void arrangeUiSettings(); public Q_SLOTS: diff --git a/DRAMSys/traceAnalyzer/presentation/tracenavigator.cpp b/DRAMSys/traceAnalyzer/presentation/tracenavigator.cpp index 13b0526c..f5ab5a0b 100644 --- a/DRAMSys/traceAnalyzer/presentation/tracenavigator.cpp +++ b/DRAMSys/traceAnalyzer/presentation/tracenavigator.cpp @@ -39,14 +39,24 @@ #include "tracenavigator.h" #include "util/traceplotlinecache.h" #include "vector" +#include "businessObjects/commentmodel.h" +#include +#include using namespace std; -TraceNavigator::TraceNavigator(QString path, QObject *parent) - : QObject(parent), traceFile(path, true), changesToCommitExist(false) +TraceNavigator::TraceNavigator(QString path, CommentModel *commentModel, QObject *parent) + : QObject(parent), traceFile(path, true), commentModel(commentModel), changesToCommitExist(false) { getCommentsFromDB(); + QObject::connect(commentModel, &CommentModel::gotoCommentTriggered, this, [=](const QModelIndex &index){ + navigateToTime(commentModel->getTimeFromIndex(index)); + }); + + QObject::connect(commentModel, &CommentModel::dataChanged, this, &TraceNavigator::traceFileModified); + QObject::connect(commentModel, &CommentModel::rowsRemoved, this, &TraceNavigator::traceFileModified); + tracePlotLineCache = std::make_shared(getTracePlotLines(), GeneralTraceInfo().numberOfRanks, GeneralTraceInfo().groupsPerRank, GeneralTraceInfo().banksPerGroup); @@ -79,49 +89,20 @@ void TraceNavigator::navigateToTransaction(ID id) navigateToTime(traceFile.getTransactionByID(id)->span.Begin()); } - -/* Comment and debug messages - * - */ -void TraceNavigator::insertComment(const Comment &comment) -{ - comments.emplace(comment.Time(), comment); - changesToCommitExist = true; - Q_EMIT commentsChanged(); -} - -void TraceNavigator::removeCommentAtTime(traceTime time) -{ - auto found = comments.find(time); - if (found != comments.end()) { - comments.erase(found); - changesToCommitExist = true; - Q_EMIT commentsChanged(); - } -} - - - /* DB * */ void TraceNavigator::commitChangesToDB() { - vector commentsToInsert; - for (const auto &pair : comments) { - commentsToInsert.push_back(pair.second); - } - - traceFile.updateComments(commentsToInsert); + traceFile.updateComments(commentModel->getComments()); changesToCommitExist = false; } void TraceNavigator::getCommentsFromDB() { - for (const Comment &comment : traceFile.getComments()) { - comments.emplace(comment.Time(), comment); - } + for (const auto &comment : traceFile.getComments()) + commentModel->addComment(comment.time, comment.text); } void TraceNavigator::refreshData() @@ -334,3 +315,18 @@ std::shared_ptr TraceNavigator::getTracePlotLineCache() { return tracePlotLineCache; } + +const CommentModel *TraceNavigator::getCommentModel() const +{ + return commentModel; +} + +bool TraceNavigator::existChangesToCommit() const +{ + return changesToCommitExist; +} + +void TraceNavigator::traceFileModified() +{ + changesToCommitExist = true; +} diff --git a/DRAMSys/traceAnalyzer/presentation/tracenavigator.h b/DRAMSys/traceAnalyzer/presentation/tracenavigator.h index 28bc8af7..f8a89bf1 100644 --- a/DRAMSys/traceAnalyzer/presentation/tracenavigator.h +++ b/DRAMSys/traceAnalyzer/presentation/tracenavigator.h @@ -43,10 +43,10 @@ #include "data/tracedb.h" #include "businessObjects/generalinfo.h" #include "businessObjects/transaction.h" -#include "businessObjects/comment.h" #include "memory" class TracePlotLineCache; +class CommentModel; /* Class to navigate through a tracefile * @@ -56,10 +56,9 @@ class TracePlotLineCache; class TraceNavigator : public QObject { Q_OBJECT - using CommentMap = std::map; public: - TraceNavigator(QString path, QObject *parent = 0); + TraceNavigator(QString path, CommentModel *commentModel, QObject *parent = 0); ~TraceNavigator(); traceTime CurrentTraceTime() const @@ -111,23 +110,22 @@ public: bool transactionIsSelected(const std::shared_ptr &Transaction) const; - void insertComment(const Comment &comment); - const CommentMap &getComments() - { - return comments; - } - void removeCommentAtTime(traceTime time); - void commitChangesToDB(); void refreshData(); std::shared_ptr getTracePlotLines(); std::shared_ptr getTracePlotLineCache(); + const CommentModel *getCommentModel() const; + + bool existChangesToCommit() const; + Q_SIGNALS: void currentTraceTimeChanged(); void selectedTransactionsChanged(); - void commentsChanged(); + +public Q_SLOTS: + void traceFileModified(); private: TraceDB traceFile; @@ -136,7 +134,9 @@ private: //components drawing the tracefile center around that time traceTime currentTraceTime = 0; std::vector> selectedTransactions; - CommentMap comments; + + CommentModel *commentModel; + void getCommentsFromDB(); bool changesToCommitExist; diff --git a/DRAMSys/traceAnalyzer/presentation/traceplot.cpp b/DRAMSys/traceAnalyzer/presentation/traceplot.cpp index fef381a1..c6d4133e 100644 --- a/DRAMSys/traceAnalyzer/presentation/traceplot.cpp +++ b/DRAMSys/traceAnalyzer/presentation/traceplot.cpp @@ -55,6 +55,7 @@ #include "traceplot.h" #include "gototimedialog.h" #include "tracedrawing.h" +#include "businessObjects/commentmodel.h" #include "util/engineeringScaleDraw.h" #include "util/clkgrid.h" #include "util/customlabelscaledraw.h" @@ -184,12 +185,24 @@ void TracePlot::setUpContextMenu() contextMenu->addActions({showQueryEditor, insertComment, exportToPdf, toggleCollapsedState}); } -void TracePlot::init(TraceNavigator *navigator, QScrollBar *scrollBar) +void TracePlot::init(TraceNavigator *navigator, QScrollBar *scrollBar, CommentModel *commentModel) { Q_ASSERT(isInitialized == false); isInitialized = true; this->scrollBar = scrollBar; + this->commentModel = commentModel; + + installEventFilter(commentModel); + + QObject::connect(commentModel, &CommentModel::dataChanged, + this, &TracePlot::commentsChanged); + + QObject::connect(commentModel, &CommentModel::rowsRemoved, + this, &TracePlot::commentsChanged); + + QObject::connect(commentModel->selectionModel(), &QItemSelectionModel::selectionChanged, + this, &TracePlot::commentsChanged); this->navigator = navigator; connectNavigatorQ_SIGNALS(); @@ -216,8 +229,6 @@ void TracePlot::connectNavigatorQ_SIGNALS() SLOT(currentTraceTimeChanged())); QObject::connect(navigator, SIGNAL(selectedTransactionsChanged()), this, SLOT(selectedTransactionsChanged())); - QObject::connect(navigator, SIGNAL(commentsChanged()), this, - SLOT(commentsChanged())); } void TracePlot::setUpDrawingProperties() @@ -343,16 +354,23 @@ 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); + QList selectedRows = commentModel->selectionModel()->selectedRows(); + + for (int row = 0; row < commentModel->rowCount(); row++) + { + QModelIndex timeIndex = commentModel->index(row, static_cast(CommentModel::Column::Time)); + QModelIndex textIndex = commentModel->index(row, static_cast(CommentModel::Column::Comment)); + + bool selected = std::find(selectedRows.begin(), selectedRows.end(), commentModel->index(row, 0)) != selectedRows.end(); + + QwtPlotMarker *marker = new QwtPlotMarker(); + marker->setLabel(textIndex.data().toString()); + marker->setLabelOrientation(Qt::Vertical); + marker->setLabelAlignment(Qt::AlignLeft | Qt::AlignBottom); + marker->setXValue(static_cast(timeIndex.data(Qt::UserRole).toLongLong())); + marker->setLineStyle(QwtPlotMarker::LineStyle::VLine); + marker->setLinePen(QColor(selected ? Qt::red : Qt::blue), 2); + marker->attach(this); } } @@ -541,13 +559,7 @@ void TracePlot::on_deselectAll() void TracePlot::on_insertComment() { traceTime time = invTransform(xBottom, contextMenuMouseDown.x()); - bool ok; - QString text = QInputDialog::getText(this, QString("Insert comment"), - QString("Insert comment at ") + prettyFormatTime(time), QLineEdit::Normal, - QString(), &ok); - if (ok) { - navigator->insertComment(Comment(time, text)); - } + commentModel->addComment(time); } void TracePlot::on_goToTime() @@ -646,9 +658,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 +720,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 +733,35 @@ void TracePlot::SelectTransaction(int x, int y) } } +void TracePlot::SelectComment(int x) const +{ + Timespan timespan = hoveredTimespan(x); + QModelIndex index = commentModel->hoveredComment(timespan); + + if (!index.isValid()) + return; + + if (keyPressData.ctrlPressed) + commentModel->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Toggle | QItemSelectionModel::Rows); + else + commentModel->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); +} + +Timespan TracePlot::hoveredTimespan(int x) const +{ + traceTime time = invTransform(xBottom, x); + traceTime offset = 0.01 * zoomLevel; + + return Timespan(time - offset, time + offset); +} + void TracePlot::openContextMenu(const QPoint &pos, const QPoint &mouseDown) { contextMenuMouseDown = mouseDown; - contextMenu->exec(pos); + Timespan timespan = hoveredTimespan(mouseDown.x()); + + if (commentModel->hoveredComment(timespan).isValid()) + commentModel->openContextMenu(); + else + contextMenu->exec(pos); } - - diff --git a/DRAMSys/traceAnalyzer/presentation/traceplot.h b/DRAMSys/traceAnalyzer/presentation/traceplot.h index 2676988d..bea93693 100644 --- a/DRAMSys/traceAnalyzer/presentation/traceplot.h +++ b/DRAMSys/traceAnalyzer/presentation/traceplot.h @@ -63,6 +63,7 @@ class TracePlotMouseLabel; class CustomLabelScaleDraw; class ToggleCollapsedAction; +class CommentModel; class TracePlot : public QwtPlot { @@ -71,7 +72,7 @@ class TracePlot : public QwtPlot public: TracePlot(QWidget *parent = NULL); - void init(TraceNavigator *navigator, QScrollBar *scrollBar); + void init(TraceNavigator *navigator, QScrollBar *scrollBar, CommentModel *commentModel); Timespan GetCurrentTimespan(); traceTime ZoomLevel() const { @@ -97,6 +98,7 @@ Q_SIGNALS: void tracePlotZoomChanged(); void tracePlotLinesChanged(); void colorGroupingChanged(ColorGrouping colorgrouping); + // void editComment private Q_SLOTS: void on_executeQuery(); @@ -129,6 +131,7 @@ private: QMenu *contextMenu; QScrollBar *scrollBar; CustomLabelScaleDraw *customLabelScaleDraw; + CommentModel *commentModel; void setUpTracePlotItem(); void setUpDrawingProperties(); @@ -186,7 +189,10 @@ 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; + Timespan hoveredTimespan(int x) 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..12d34018 100644 --- a/DRAMSys/traceAnalyzer/presentation/tracescroller.cpp +++ b/DRAMSys/traceAnalyzer/presentation/tracescroller.cpp @@ -62,6 +62,17 @@ void TraceScroller::init(TraceNavigator *navigator, TracePlot *tracePlot) this -> navigator = navigator; connectNavigatorQ_SIGNALS(); + const CommentModel *commentModel = navigator->getCommentModel(); + + QObject::connect(commentModel, &CommentModel::dataChanged, + this, &TraceScroller::commentsChanged); + + QObject::connect(commentModel, &CommentModel::rowsRemoved, + this, &TraceScroller::commentsChanged); + + QObject::connect(commentModel->selectionModel(), &QItemSelectionModel::selectionChanged, + this, &TraceScroller::commentsChanged); + setUpDrawingProperties(); setUpAxis(); setUpTracePlotItem(); @@ -119,8 +130,7 @@ void TraceScroller::connectNavigatorQ_SIGNALS() { QObject::connect(navigator, SIGNAL(currentTraceTimeChanged()), this, SLOT(currentTraceTimeChanged())); - QObject::connect(navigator, SIGNAL(commentsChanged()), this, - SLOT(commentsChanged())); + QObject::connect(navigator, SIGNAL(selectedTransactionsChanged()), this, SLOT(selectedTransactionsChanged())); } @@ -147,12 +157,19 @@ Timespan TraceScroller::GetCurrentTimespan() void TraceScroller::getAndDrawComments() { - for (const auto &pair : navigator->getComments()) { - const Comment &comment = pair.second; + const CommentModel *commentModel = navigator->getCommentModel(); + QList selectedRows = commentModel->selectionModel()->selectedRows(); + + for (int row = 0; row < commentModel->rowCount(); row++) + { + QModelIndex timeIndex = commentModel->index(row, static_cast(CommentModel::Column::Time)); + + bool selected = std::find(selectedRows.begin(), selectedRows.end(), commentModel->index(row, 0)) != selectedRows.end(); + QwtPlotMarker *maker = new QwtPlotMarker(); - maker->setXValue(static_cast(comment.Time())); + maker->setXValue(static_cast(timeIndex.data(Qt::UserRole).toLongLong())); maker->setLineStyle(QwtPlotMarker::LineStyle::VLine); - maker->setLinePen(QColor(Qt::blue), 2); + maker->setLinePen(QColor(selected ? Qt::red : Qt::blue), 2); maker->attach(this); } } diff --git a/DRAMSys/traceAnalyzer/presentation/tracescroller.h b/DRAMSys/traceAnalyzer/presentation/tracescroller.h index 8a5d8583..310d46d1 100644 --- a/DRAMSys/traceAnalyzer/presentation/tracescroller.h +++ b/DRAMSys/traceAnalyzer/presentation/tracescroller.h @@ -60,7 +60,6 @@ private: void setUpAxis(); void connectNavigatorQ_SIGNALS(); - void getAndDrawComments(); QwtPlotZoneItem *canvasClip; traceTime zoomLevel; diff --git a/DRAMSys/traceAnalyzer/traceanalyzer.cpp b/DRAMSys/traceAnalyzer/traceanalyzer.cpp index 02103ce7..dda259b9 100644 --- a/DRAMSys/traceAnalyzer/traceanalyzer.cpp +++ b/DRAMSys/traceAnalyzer/traceanalyzer.cpp @@ -37,12 +37,13 @@ */ #include "traceanalyzer.h" -#include "ui_traceanalyzer.h" +#include "QMessageBox" #include "tracefiletab.h" +#include "ui_traceanalyzer.h" +#include +#include #include #include -#include -#include "QMessageBox" #include void TraceAnalyzer::setUpStatusBar() @@ -64,13 +65,6 @@ TraceAnalyzer::TraceAnalyzer(QWidget *parent) : ui(new Ui::TraceAnalyzer) { setUpGui(); - // Disable actions except for "Open" until some file is open. - ui->actionReload_all->setEnabled(false); - ui->actionSaveChangesToDB->setEnabled(false); - ui->actionExportAsVCD->setEnabled(false); - ui->actionClose_all->setEnabled(false); - ui->actionTest->setEnabled(false); - ui->actionMetrics->setEnabled(false); } TraceAnalyzer::TraceAnalyzer(QSet paths, StartupOption option, @@ -119,41 +113,43 @@ void TraceAnalyzer::openTracefile(const QString &path) ui->traceFileTabs->addTab(tab, QFileInfo(path).baseName()); openedTraceFiles.insert(path); - // Enable actions - ui->actionReload_all->setEnabled(true); - ui->actionSaveChangesToDB->setEnabled(true); - - if (PythonCaller::instance().vcdExportDependenciesAvailable()) - ui->actionExportAsVCD->setEnabled(true); - - ui->actionClose_all->setEnabled(true); - ui->actionTest->setEnabled(true); - ui->actionMetrics->setEnabled(true); - statusLabel->clear(); } +void TraceAnalyzer::on_menuFile_aboutToShow() +{ + ui->actionOpen->setEnabled(true); + + bool tabsOpen = ui->traceFileTabs->count() > 0; + bool exportAsVcdAvailable = PythonCaller::instance().vcdExportDependenciesAvailable(); + + ui->actionReload_all->setEnabled(tabsOpen); + ui->actionSaveChangesToDB->setEnabled(tabsOpen); + ui->actionExportAsVCD->setEnabled(tabsOpen && exportAsVcdAvailable); + ui->actionClose_all->setEnabled(tabsOpen); + ui->actionTest->setEnabled(tabsOpen); + ui->actionMetrics->setEnabled(tabsOpen); +} + void TraceAnalyzer::on_traceFileTabs_tabCloseRequested(int index) { TraceFileTab *traceFileTab = static_cast (ui->traceFileTabs->widget(index)); - openedTraceFiles.remove(traceFileTab->getPathToTraceFile()); - ui->traceFileTabs->removeTab(index); - delete traceFileTab; + + if (traceFileTab->close()) + { + openedTraceFiles.remove(traceFileTab->getPathToTraceFile()); + ui->traceFileTabs->removeTab(index); + delete traceFileTab; + } } void TraceAnalyzer::on_actionClose_all_triggered() { - while (ui->traceFileTabs->count()) - on_traceFileTabs_tabCloseRequested(0); - - // All files closed. Disable actions except for "Open". - ui->actionReload_all->setEnabled(false); - ui->actionSaveChangesToDB->setEnabled(false); - ui->actionExportAsVCD->setEnabled(false); - ui->actionClose_all->setEnabled(false); - ui->actionTest->setEnabled(false); - ui->actionMetrics->setEnabled(false); + for (unsigned int i = ui->traceFileTabs->count(); i--;) + { + on_traceFileTabs_tabCloseRequested(i); + } statusLabel->clear(); } @@ -163,23 +159,24 @@ void TraceAnalyzer::on_actionReload_all_triggered() TraceFileTab *traceFileTab; // Remove all tabs - int tabIndex = 0; - while (ui->traceFileTabs->count() != 0) { + for (unsigned int i = ui->traceFileTabs->count(); i--;) + { traceFileTab = static_cast(ui->traceFileTabs->widget(0)); - std::cout << "Closing tab #" << tabIndex << " \"" << - traceFileTab->getPathToTraceFile().toStdString() << "\"" << std::endl; + std::cout << "Closing tab #" << i << " \"" << traceFileTab->getPathToTraceFile().toStdString() << "\"" + << std::endl; + + if (!traceFileTab->close()) + return; ui->traceFileTabs->removeTab(0); delete traceFileTab; - - tabIndex++; } QList list = openedTraceFiles.toList(); qSort(list); // Recreate all tabs - tabIndex = 0; + int tabIndex = 0; for (auto path : list) { std::cout << "Reopening tab# " << tabIndex << " \"" << path.toStdString() << "\"" << std::endl; @@ -231,3 +228,18 @@ void TraceAnalyzer::on_actionMetrics_triggered() evaluationTool.activateWindow(); evaluationTool.showAndEvaluateMetrics(openedTraceFiles.toList()); } + +void TraceAnalyzer::closeEvent(QCloseEvent *event) +{ + for (unsigned int i = 0; i < ui->traceFileTabs->count(); i++) + { + QWidget *tab = ui->traceFileTabs->widget(i); + if (!tab->close()) + { + event->ignore(); + return; + } + } + + event->accept(); +} diff --git a/DRAMSys/traceAnalyzer/traceanalyzer.h b/DRAMSys/traceAnalyzer/traceanalyzer.h index f28ae7e2..e6916862 100644 --- a/DRAMSys/traceAnalyzer/traceanalyzer.h +++ b/DRAMSys/traceAnalyzer/traceanalyzer.h @@ -67,20 +67,24 @@ public: void setUpStatusBar(); void setUpGui(); +protected: + void closeEvent(QCloseEvent *event) override; + private: void openTracefile(const QString &path); QLabel *statusLabel; QSet openedTraceFiles; private Q_SLOTS: + void on_menuFile_aboutToShow(); + void on_traceFileTabs_tabCloseRequested(int index); + void on_actionOpen_triggered(); void on_actionReload_all_triggered(); void on_actionSaveChangesToDB_triggered(); void on_actionExportAsVCD_triggered(); - void on_traceFileTabs_tabCloseRequested(int index); void on_actionClose_all_triggered(); void on_actionTest_triggered(); - void on_actionMetrics_triggered(); public Q_SLOTS: diff --git a/DRAMSys/traceAnalyzer/tracefiletab.cpp b/DRAMSys/traceAnalyzer/tracefiletab.cpp index 36c95509..80da307a 100644 --- a/DRAMSys/traceAnalyzer/tracefiletab.cpp +++ b/DRAMSys/traceAnalyzer/tracefiletab.cpp @@ -37,37 +37,37 @@ */ #include "tracefiletab.h" -#include "ui_tracefiletab.h" -#include "queryeditor.h" #include "QFileInfo" -#include "qmessagebox.h" +#include "businessObjects/commentmodel.h" +#include "businessObjects/configmodels.h" #include "businessObjects/pythoncaller.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "qwt_plot_histogram.h" -#include "qwt_plot_curve.h" -#include "qwt_plot_layout.h" -#include "qwt_scale_draw.h" -#include "qwt_scale_widget.h" +#include "presentation/traceselector.h" +#include "qmessagebox.h" +#include "queryeditor.h" #include "qwt_legend.h" +#include "qwt_plot_curve.h" +#include "qwt_plot_histogram.h" +#include "qwt_plot_layout.h" #include "qwt_plot_magnifier.h" #include "qwt_plot_panner.h" -#include "presentation/traceselector.h" -#include "businessObjects/configmodels.h" +#include "qwt_scale_draw.h" +#include "qwt_scale_widget.h" +#include "ui_tracefiletab.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include - -TraceFileTab::TraceFileTab(QWidget *parent, const QString &path) : - QWidget(parent), ui(new Ui::TraceFileTab), navigator(new TraceNavigator(path, this)), - mcConfigModel(new McConfigModel(navigator->TraceFile(), this)), - memSpecModel(new MemSpecModel(navigator->TraceFile(), this)), - savingChangesToDB(false) +TraceFileTab::TraceFileTab(QWidget *parent, const QString &path) + : QWidget(parent), ui(new Ui::TraceFileTab), commentModel(new CommentModel(this)), + navigator(new TraceNavigator(path, commentModel, this)), + mcConfigModel(new McConfigModel(navigator->TraceFile(), this)), + memSpecModel(new MemSpecModel(navigator->TraceFile(), this)), savingChangesToDB(false) { ui->setupUi(this); this->path = path; @@ -79,6 +79,7 @@ TraceFileTab::TraceFileTab(QWidget *parent, const QString &path) : setUpFileWatcher(path); setUpTraceplotScrollbar(); setUpTraceSelector(); + setUpCommentView(); ui->fileDescriptionEdit->setPlainText( navigator->GeneralTraceInfo().description); @@ -132,7 +133,7 @@ void TraceFileTab::setUpTraceplotScrollbar() void TraceFileTab::initNavigatorAndItsDependentWidgets(QString path) { - ui->traceplot->init(navigator, ui->traceplotScrollbar); + ui->traceplot->init(navigator, ui->traceplotScrollbar, commentModel); ui->traceScroller->init(navigator, ui->traceplot); connect(this, SIGNAL(colorGroupingChanged(ColorGrouping)), @@ -140,7 +141,6 @@ void TraceFileTab::initNavigatorAndItsDependentWidgets(QString path) ui->selectedTransactionTree->init(navigator); //ui->debugMessages->init(navigator,ui->traceplot); - ui->commentTree->init(navigator); } void TraceFileTab::setUpFileWatcher(QString path) @@ -158,6 +158,26 @@ void TraceFileTab::setUpTraceSelector() selector->init(ui->traceplot); } +void TraceFileTab::setUpCommentView() +{ + ui->commentView->setModel(commentModel); + ui->commentView->setSelectionModel(commentModel->selectionModel()); + ui->commentView->installEventFilter(commentModel); + ui->commentView->setContextMenuPolicy(Qt::CustomContextMenu); + + QObject::connect(ui->commentView, &QTableView::customContextMenuRequested, + commentModel, &CommentModel::openContextMenu); + + QObject::connect(commentModel, &CommentModel::editTriggered, ui->commentView, [=](const QModelIndex &index){ + ui->tabWidget->setCurrentWidget(ui->tabSelectedTrans); + ui->commentView->edit(index); + ui->commentView->scrollTo(index); + }); + + QObject::connect(ui->commentView, &QTableView::doubleClicked, + commentModel, &CommentModel::rowDoubleClicked); +} + void TraceFileTab::tracefileChanged() { if (savingChangesToDB == true) { @@ -176,6 +196,37 @@ void TraceFileTab::tracefileChanged() navigator->refreshData(); } +void TraceFileTab::closeEvent(QCloseEvent *event) +{ + if (navigator->existChangesToCommit()) + { + QMessageBox saveDialog; + saveDialog.setWindowTitle(QFileInfo(path).baseName()); + saveDialog.setText("The trace file has been modified."); + saveDialog.setInformativeText("Do you want to save your changes?
Unsaved changes will be lost."); + saveDialog.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + saveDialog.setDefaultButton(QMessageBox::Save); + saveDialog.setIcon(QMessageBox::Warning); + int returnCode = saveDialog.exec(); + + switch (returnCode) + { + case QMessageBox::Cancel: + event->ignore(); + break; + case QMessageBox::Discard: + event->accept(); + break; + case QMessageBox::Save: + commitChangesToDB(); + event->accept(); + break; + }; + } + else + event->accept(); +} + class ItemDelegate: public QItemDelegate { public: diff --git a/DRAMSys/traceAnalyzer/tracefiletab.h b/DRAMSys/traceAnalyzer/tracefiletab.h index 966d5784..1962088d 100644 --- a/DRAMSys/traceAnalyzer/tracefiletab.h +++ b/DRAMSys/traceAnalyzer/tracefiletab.h @@ -47,6 +47,8 @@ #include "presentation/tracescroller.h" #include "businessObjects/configmodels.h" +class CommentModel; + namespace Ui { class TraceFileTab; } @@ -62,6 +64,7 @@ public: void setUpFileWatcher(QString filename); void setUpTraceplotScrollbar(); void setUpTraceSelector(); + void setUpCommentView(); void initNavigatorAndItsDependentWidgets(QString path); QString getPathToTraceFile() { @@ -70,12 +73,17 @@ public: void commitChangesToDB(void); void exportAsVCD(); +protected: + void closeEvent(QCloseEvent *event) override; + private: QString path; Ui::TraceFileTab *ui; - TraceNavigator *navigator; QFileSystemWatcher *fileWatcher; + CommentModel *commentModel; + TraceNavigator *navigator; + QAbstractItemModel *mcConfigModel; QAbstractItemModel *memSpecModel; diff --git a/DRAMSys/traceAnalyzer/tracefiletab.ui b/DRAMSys/traceAnalyzer/tracefiletab.ui index 64693bdb..647a3b50 100644 --- a/DRAMSys/traceAnalyzer/tracefiletab.ui +++ b/DRAMSys/traceAnalyzer/tracefiletab.ui @@ -170,7 +170,7 @@ - + 2 @@ -189,11 +189,27 @@ 16777215 - - - 1 - - + + QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed + + + true + + + QAbstractItemView::SelectRows + + + Qt::NoPen + + + false + + + true + + + false + @@ -402,11 +418,6 @@ QListView
presentation/tracescroller.h
- - CommentTreeWidget - QTreeWidget -
presentation/commenttreewidget.h
-
SelectedTransactionTreeWidget QTreeWidget