forked from qt-creator/qt-creator
Don't accidentally reuse the EventViewItem class from the events view, drop the column number as it's always -1, and actually use the file name for sorting instead of random junk. Also add all the missing bits necessary for sorting all tables by any column and drop the hash string as it's useless. Change-Id: I26dcdd229f1f1ef18f68edcb6edfb0427a2e2e6f Reviewed-by: Kai Koehne <kai.koehne@digia.com>
717 lines
24 KiB
C++
717 lines
24 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
|
** Contact: http://www.qt-project.org/legal
|
|
**
|
|
** This file is part of Qt Creator.
|
|
**
|
|
** Commercial License Usage
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
** accordance with the commercial license agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and Digia. For licensing terms and
|
|
** conditions see http://qt.digia.com/licensing. For further information
|
|
** use the contact form at http://qt.digia.com/contact-us.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
** General Public License version 2.1 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** In addition, as a special exception, Digia gives you certain additional
|
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "qv8profilereventview.h"
|
|
|
|
#include <QUrl>
|
|
#include <QHash>
|
|
|
|
#include <QStandardItem>
|
|
#include <QHeaderView>
|
|
|
|
#include <QApplication>
|
|
#include <QClipboard>
|
|
|
|
#include <QContextMenuEvent>
|
|
#include <QDebug>
|
|
|
|
#include <coreplugin/minisplitter.h>
|
|
#include <QVBoxLayout>
|
|
#include <QHBoxLayout>
|
|
|
|
#include "qmlprofilerviewmanager.h"
|
|
#include "qmlprofilertool.h"
|
|
#include "qv8profilerdatamodel.h"
|
|
#include <QMenu>
|
|
|
|
#include <utils/qtcassert.h>
|
|
|
|
using namespace QmlDebug;
|
|
|
|
namespace QmlProfiler {
|
|
namespace Internal {
|
|
|
|
enum ItemRole {
|
|
SortRole = Qt::UserRole + 1, // Sort by data, not by displayed text
|
|
FilenameRole,
|
|
LineRole,
|
|
EventIdRole
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class V8ViewItem : public QStandardItem
|
|
{
|
|
public:
|
|
V8ViewItem(const QString &text) : QStandardItem(text) {}
|
|
|
|
virtual bool operator<(const QStandardItem &other) const
|
|
{
|
|
// first column is special
|
|
if (column() == 0) {
|
|
int filenameDiff = QUrl(data(FilenameRole).toString()).fileName().compare(
|
|
QUrl(other.data(FilenameRole).toString()).fileName(), Qt::CaseInsensitive);
|
|
return filenameDiff != 0 ? filenameDiff < 0 :
|
|
data(LineRole).toInt() < other.data(LineRole).toInt();
|
|
} else if (data(SortRole).type() == QVariant::String) {
|
|
// Strings should be case-insensitive compared
|
|
return data(SortRole).toString().compare(other.data(SortRole).toString(),
|
|
Qt::CaseInsensitive) < 0;
|
|
} else {
|
|
// For everything else the standard comparison should be OK
|
|
return QStandardItem::operator<(other);
|
|
}
|
|
}
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class QV8ProfilerEventsWidget::QV8ProfilerEventsWidgetPrivate
|
|
{
|
|
public:
|
|
QV8ProfilerEventsWidgetPrivate(QV8ProfilerEventsWidget *qq):q(qq) {}
|
|
~QV8ProfilerEventsWidgetPrivate() {}
|
|
|
|
QV8ProfilerEventsWidget *q;
|
|
|
|
Analyzer::IAnalyzerTool *m_profilerTool;
|
|
QmlProfilerViewManager *m_viewContainer;
|
|
|
|
QV8ProfilerEventsMainView *m_eventTree;
|
|
QV8ProfilerEventRelativesView *m_eventChildren;
|
|
QV8ProfilerEventRelativesView *m_eventParents;
|
|
|
|
QV8ProfilerDataModel *v8Model;
|
|
};
|
|
|
|
QV8ProfilerEventsWidget::QV8ProfilerEventsWidget(QWidget *parent,
|
|
Analyzer::IAnalyzerTool *profilerTool,
|
|
QmlProfilerViewManager *container,
|
|
QmlProfilerModelManager *profilerModelManager )
|
|
: QWidget(parent), d(new QV8ProfilerEventsWidgetPrivate(this))
|
|
{
|
|
setObjectName(QLatin1String("QmlProfilerV8ProfileView"));
|
|
|
|
d->v8Model = profilerModelManager->v8Model();
|
|
|
|
d->m_eventTree = new QV8ProfilerEventsMainView(this, d->v8Model);
|
|
connect(d->m_eventTree, SIGNAL(gotoSourceLocation(QString,int,int)), this, SIGNAL(gotoSourceLocation(QString,int,int)));
|
|
|
|
d->m_eventChildren = new QV8ProfilerEventRelativesView(d->v8Model,
|
|
QV8ProfilerEventRelativesView::ChildrenView,
|
|
this);
|
|
d->m_eventParents = new QV8ProfilerEventRelativesView(d->v8Model,
|
|
QV8ProfilerEventRelativesView::ParentsView,
|
|
this);
|
|
connect(d->m_eventTree, SIGNAL(eventSelected(int)), d->m_eventChildren, SLOT(displayEvent(int)));
|
|
connect(d->m_eventTree, SIGNAL(eventSelected(int)), d->m_eventParents, SLOT(displayEvent(int)));
|
|
connect(d->m_eventChildren, SIGNAL(eventClicked(int)), d->m_eventTree, SLOT(selectEvent(int)));
|
|
connect(d->m_eventParents, SIGNAL(eventClicked(int)), d->m_eventTree, SLOT(selectEvent(int)));
|
|
connect(d->v8Model, SIGNAL(changed()), this, SLOT(updateEnabledState()));
|
|
|
|
// widget arrangement
|
|
QVBoxLayout *groupLayout = new QVBoxLayout;
|
|
groupLayout->setContentsMargins(0,0,0,0);
|
|
groupLayout->setSpacing(0);
|
|
|
|
Core::MiniSplitter *splitterVertical = new Core::MiniSplitter;
|
|
splitterVertical->addWidget(d->m_eventTree);
|
|
Core::MiniSplitter *splitterHorizontal = new Core::MiniSplitter;
|
|
splitterHorizontal->addWidget(d->m_eventParents);
|
|
splitterHorizontal->addWidget(d->m_eventChildren);
|
|
splitterHorizontal->setOrientation(Qt::Horizontal);
|
|
splitterVertical->addWidget(splitterHorizontal);
|
|
splitterVertical->setOrientation(Qt::Vertical);
|
|
splitterVertical->setStretchFactor(0,5);
|
|
splitterVertical->setStretchFactor(1,2);
|
|
groupLayout->addWidget(splitterVertical);
|
|
setLayout(groupLayout);
|
|
|
|
d->m_profilerTool = profilerTool;
|
|
d->m_viewContainer = container;
|
|
setEnabled(false);
|
|
}
|
|
|
|
QV8ProfilerEventsWidget::~QV8ProfilerEventsWidget()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void QV8ProfilerEventsWidget::updateEnabledState()
|
|
{
|
|
setEnabled(!d->v8Model->isEmpty());
|
|
}
|
|
|
|
void QV8ProfilerEventsWidget::clear()
|
|
{
|
|
d->m_eventTree->clear();
|
|
d->m_eventChildren->clear();
|
|
d->m_eventParents->clear();
|
|
setEnabled(false);
|
|
}
|
|
|
|
QModelIndex QV8ProfilerEventsWidget::selectedItem() const
|
|
{
|
|
return d->m_eventTree->selectedItem();
|
|
}
|
|
|
|
void QV8ProfilerEventsWidget::contextMenuEvent(QContextMenuEvent *ev)
|
|
{
|
|
QTC_ASSERT(d->m_viewContainer, return;);
|
|
|
|
QMenu menu;
|
|
QAction *copyRowAction = 0;
|
|
QAction *copyTableAction = 0;
|
|
|
|
QmlProfilerTool *profilerTool = qobject_cast<QmlProfilerTool *>(d->m_profilerTool);
|
|
QPoint position = ev->globalPos();
|
|
|
|
if (profilerTool) {
|
|
QList <QAction *> commonActions = profilerTool->profilerContextMenuActions();
|
|
foreach (QAction *act, commonActions) {
|
|
menu.addAction(act);
|
|
}
|
|
}
|
|
|
|
if (mouseOnTable(position)) {
|
|
menu.addSeparator();
|
|
if (selectedItem().isValid())
|
|
copyRowAction = menu.addAction(QCoreApplication::translate("QmlProfiler::Internal::QmlProfilerEventsWidget", "Copy Row"));
|
|
copyTableAction = menu.addAction(QCoreApplication::translate("QmlProfiler::Internal::QmlProfilerEventsWidget", "Copy Table"));
|
|
}
|
|
|
|
QAction *selectedAction = menu.exec(position);
|
|
|
|
if (selectedAction) {
|
|
if (selectedAction == copyRowAction)
|
|
copyRowToClipboard();
|
|
if (selectedAction == copyTableAction)
|
|
copyTableToClipboard();
|
|
}
|
|
}
|
|
|
|
void QV8ProfilerEventsWidget::resizeEvent(QResizeEvent *event)
|
|
{
|
|
QWidget::resizeEvent(event);
|
|
emit resized();
|
|
}
|
|
|
|
bool QV8ProfilerEventsWidget::mouseOnTable(const QPoint &position) const
|
|
{
|
|
QPoint tableTopLeft = d->m_eventTree->mapToGlobal(QPoint(0,0));
|
|
QPoint tableBottomRight = d->m_eventTree->mapToGlobal(QPoint(d->m_eventTree->width(), d->m_eventTree->height()));
|
|
return (position.x() >= tableTopLeft.x() && position.x() <= tableBottomRight.x() && position.y() >= tableTopLeft.y() && position.y() <= tableBottomRight.y());
|
|
}
|
|
|
|
void QV8ProfilerEventsWidget::copyTableToClipboard() const
|
|
{
|
|
d->m_eventTree->copyTableToClipboard();
|
|
}
|
|
|
|
void QV8ProfilerEventsWidget::copyRowToClipboard() const
|
|
{
|
|
d->m_eventTree->copyRowToClipboard();
|
|
}
|
|
|
|
void QV8ProfilerEventsWidget::updateSelectedEvent(int eventId) const
|
|
{
|
|
if (d->m_eventTree->selectedEventId() != eventId)
|
|
d->m_eventTree->selectEvent(eventId);
|
|
}
|
|
|
|
void QV8ProfilerEventsWidget::selectBySourceLocation(const QString &filename, int line, int column)
|
|
{
|
|
// This slot is used to connect the javascript pane with the qml events pane
|
|
// Our javascript trace data does not store column information
|
|
// thus we ignore it here
|
|
Q_UNUSED(column);
|
|
d->m_eventTree->selectEventByLocation(filename, line);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class QV8ProfilerEventsMainView::QV8ProfilerEventsMainViewPrivate
|
|
{
|
|
public:
|
|
QV8ProfilerEventsMainViewPrivate(QV8ProfilerEventsMainView *qq) : q(qq) {}
|
|
|
|
void buildV8ModelFromList( const QList<QV8ProfilerDataModel::QV8EventData *> &list );
|
|
int getFieldCount();
|
|
|
|
QString textForItem(QStandardItem *item, bool recursive) const;
|
|
|
|
|
|
QV8ProfilerEventsMainView *q;
|
|
|
|
QV8ProfilerDataModel *m_v8Model;
|
|
QStandardItemModel *m_model;
|
|
QList<bool> m_fieldShown;
|
|
QHash<int, int> m_columnIndex; // maps field enum to column index
|
|
int m_firstNumericColumn;
|
|
bool m_preventSelectBounce;
|
|
};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
QV8ProfilerEventsMainView::QV8ProfilerEventsMainView(QWidget *parent,
|
|
QV8ProfilerDataModel *v8Model)
|
|
: QmlProfilerTreeView(parent), d(new QV8ProfilerEventsMainViewPrivate(this))
|
|
{
|
|
setObjectName(QLatin1String("QmlProfilerEventsTable"));
|
|
setSortingEnabled(false);
|
|
|
|
d->m_model = new QStandardItemModel(this);
|
|
d->m_model->setSortRole(SortRole);
|
|
setModel(d->m_model);
|
|
connect(this, SIGNAL(activated(QModelIndex)), this, SLOT(jumpToItem(QModelIndex)));
|
|
|
|
d->m_v8Model = v8Model;
|
|
connect(d->m_v8Model, SIGNAL(changed()), this, SLOT(buildModel()));
|
|
d->m_firstNumericColumn = 0;
|
|
d->m_preventSelectBounce = false;
|
|
|
|
setFieldViewable(Name, true);
|
|
setFieldViewable(Type, false);
|
|
setFieldViewable(TimeInPercent, true);
|
|
setFieldViewable(TotalTime, true);
|
|
setFieldViewable(SelfTimeInPercent, true);
|
|
setFieldViewable(SelfTime, true);
|
|
setFieldViewable(CallCount, false);
|
|
setFieldViewable(TimePerCall, false);
|
|
setFieldViewable(MaxTime, false);
|
|
setFieldViewable(MinTime, false);
|
|
setFieldViewable(MedianTime, false);
|
|
setFieldViewable(Details, true);
|
|
|
|
buildModel();
|
|
}
|
|
|
|
QV8ProfilerEventsMainView::~QV8ProfilerEventsMainView()
|
|
{
|
|
clear();
|
|
delete d->m_model;
|
|
delete d;
|
|
}
|
|
|
|
void QV8ProfilerEventsMainView::setFieldViewable(Fields field, bool show)
|
|
{
|
|
if (field < MaxFields) {
|
|
int length = d->m_fieldShown.count();
|
|
if (field >= length) {
|
|
for (int i=length; i<MaxFields; i++)
|
|
d->m_fieldShown << false;
|
|
}
|
|
d->m_fieldShown[field] = show;
|
|
}
|
|
}
|
|
|
|
|
|
void QV8ProfilerEventsMainView::setHeaderLabels()
|
|
{
|
|
int fieldIndex = 0;
|
|
d->m_firstNumericColumn = 0;
|
|
|
|
d->m_columnIndex.clear();
|
|
if (d->m_fieldShown[Name]) {
|
|
d->m_columnIndex[Name] = fieldIndex;
|
|
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(Location)));
|
|
d->m_firstNumericColumn++;
|
|
}
|
|
if (d->m_fieldShown[Type]) {
|
|
d->m_columnIndex[Type] = fieldIndex;
|
|
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(Type)));
|
|
d->m_firstNumericColumn++;
|
|
}
|
|
if (d->m_fieldShown[TimeInPercent]) {
|
|
d->m_columnIndex[TimeInPercent] = fieldIndex;
|
|
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(TimeInPercent)));
|
|
}
|
|
if (d->m_fieldShown[TotalTime]) {
|
|
d->m_columnIndex[TotalTime] = fieldIndex;
|
|
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(TotalTime)));
|
|
}
|
|
if (d->m_fieldShown[SelfTimeInPercent]) {
|
|
d->m_columnIndex[Type] = fieldIndex;
|
|
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(SelfTimeInPercent)));
|
|
}
|
|
if (d->m_fieldShown[SelfTime]) {
|
|
d->m_columnIndex[SelfTime] = fieldIndex;
|
|
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(SelfTime)));
|
|
}
|
|
if (d->m_fieldShown[CallCount]) {
|
|
d->m_columnIndex[CallCount] = fieldIndex;
|
|
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(CallCount)));
|
|
}
|
|
if (d->m_fieldShown[TimePerCall]) {
|
|
d->m_columnIndex[TimePerCall] = fieldIndex;
|
|
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(TimePerCall)));
|
|
}
|
|
if (d->m_fieldShown[MedianTime]) {
|
|
d->m_columnIndex[MedianTime] = fieldIndex;
|
|
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(MedianTime)));
|
|
}
|
|
if (d->m_fieldShown[MaxTime]) {
|
|
d->m_columnIndex[MaxTime] = fieldIndex;
|
|
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(MaxTime)));
|
|
}
|
|
if (d->m_fieldShown[MinTime]) {
|
|
d->m_columnIndex[MinTime] = fieldIndex;
|
|
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(MinTime)));
|
|
}
|
|
if (d->m_fieldShown[Details]) {
|
|
d->m_columnIndex[Details] = fieldIndex;
|
|
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(Details)));
|
|
}
|
|
}
|
|
|
|
void QV8ProfilerEventsMainView::clear()
|
|
{
|
|
d->m_model->clear();
|
|
d->m_model->setColumnCount(d->getFieldCount());
|
|
|
|
setHeaderLabels();
|
|
setSortingEnabled(false);
|
|
}
|
|
|
|
int QV8ProfilerEventsMainView::QV8ProfilerEventsMainViewPrivate::getFieldCount()
|
|
{
|
|
int count = 0;
|
|
for (int i=0; i < m_fieldShown.count(); ++i)
|
|
if (m_fieldShown[i])
|
|
count++;
|
|
return count;
|
|
}
|
|
|
|
void QV8ProfilerEventsMainView::buildModel()
|
|
{
|
|
clear();
|
|
d->buildV8ModelFromList( d->m_v8Model->getV8Events() );
|
|
|
|
setRootIsDecorated(false);
|
|
setSortingEnabled(true);
|
|
sortByColumn(d->m_firstNumericColumn,Qt::DescendingOrder);
|
|
|
|
expandAll();
|
|
if (d->m_fieldShown[Name])
|
|
resizeColumnToContents(0);
|
|
|
|
if (d->m_fieldShown[Type])
|
|
resizeColumnToContents(d->m_fieldShown[Name]?1:0);
|
|
collapseAll();
|
|
}
|
|
|
|
void QV8ProfilerEventsMainView::QV8ProfilerEventsMainViewPrivate::buildV8ModelFromList(
|
|
const QList<QV8ProfilerDataModel::QV8EventData *> &list)
|
|
{
|
|
for (int index = 0; index < list.count(); index++) {
|
|
QV8ProfilerDataModel::QV8EventData *v8event = list.at(index);
|
|
QList<QStandardItem *> newRow;
|
|
|
|
if (m_fieldShown[Name])
|
|
newRow << new V8ViewItem(v8event->displayName);
|
|
|
|
if (m_fieldShown[TimeInPercent]) {
|
|
newRow << new V8ViewItem(QString::number(v8event->totalPercent,'f',2)+QLatin1String(" %"));
|
|
newRow.last()->setData(QVariant(v8event->totalPercent));
|
|
}
|
|
|
|
if (m_fieldShown[TotalTime]) {
|
|
newRow << new V8ViewItem(QmlProfilerBaseModel::formatTime(v8event->totalTime));
|
|
newRow.last()->setData(QVariant(v8event->totalTime));
|
|
}
|
|
|
|
if (m_fieldShown[SelfTimeInPercent]) {
|
|
newRow << new V8ViewItem(QString::number(v8event->SelfTimeInPercent,'f',2)+QLatin1String(" %"));
|
|
newRow.last()->setData(QVariant(v8event->SelfTimeInPercent));
|
|
}
|
|
|
|
if (m_fieldShown[SelfTime]) {
|
|
newRow << new V8ViewItem(QmlProfilerBaseModel::formatTime(v8event->selfTime));
|
|
newRow.last()->setData(QVariant(v8event->selfTime));
|
|
}
|
|
|
|
if (m_fieldShown[Details]) {
|
|
newRow << new V8ViewItem(v8event->functionName);
|
|
newRow.last()->setData(QVariant(v8event->functionName));
|
|
}
|
|
|
|
if (!newRow.isEmpty()) {
|
|
// no edit
|
|
foreach (QStandardItem *item, newRow)
|
|
item->setEditable(false);
|
|
|
|
// metadata
|
|
QStandardItem *firstItem = newRow.at(0);
|
|
firstItem->setData(QVariant(v8event->filename), FilenameRole);
|
|
firstItem->setData(QVariant(v8event->line), LineRole);
|
|
firstItem->setData(QVariant(v8event->eventId), EventIdRole);
|
|
|
|
// append
|
|
m_model->invisibleRootItem()->appendRow(newRow);
|
|
}
|
|
}
|
|
}
|
|
|
|
int QV8ProfilerEventsMainView::selectedEventId() const
|
|
{
|
|
QModelIndex index = selectedItem();
|
|
if (!index.isValid())
|
|
return -1;
|
|
QStandardItem *item = d->m_model->item(index.row(), 0);
|
|
return item->data(EventIdRole).toInt();
|
|
}
|
|
|
|
void QV8ProfilerEventsMainView::jumpToItem(const QModelIndex &index)
|
|
{
|
|
if (d->m_preventSelectBounce)
|
|
return;
|
|
|
|
d->m_preventSelectBounce = true;
|
|
QStandardItem *clickedItem = d->m_model->itemFromIndex(index);
|
|
QStandardItem *infoItem;
|
|
if (clickedItem->parent())
|
|
infoItem = clickedItem->parent()->child(clickedItem->row(), 0);
|
|
else
|
|
infoItem = d->m_model->item(index.row(), 0);
|
|
|
|
// show in editor
|
|
int line = infoItem->data(LineRole).toInt();
|
|
QString fileName = infoItem->data(FilenameRole).toString();
|
|
if (line!=-1 && !fileName.isEmpty())
|
|
emit gotoSourceLocation(fileName, line, -1);
|
|
|
|
// show in callers/callees subwindow
|
|
emit eventSelected(infoItem->data(EventIdRole).toInt());
|
|
|
|
d->m_preventSelectBounce = false;
|
|
}
|
|
|
|
void QV8ProfilerEventsMainView::selectEvent(int eventId)
|
|
{
|
|
for (int i=0; i<d->m_model->rowCount(); i++) {
|
|
QStandardItem *infoItem = d->m_model->item(i, 0);
|
|
if (infoItem->data(EventIdRole).toInt() == eventId) {
|
|
setCurrentIndex(d->m_model->indexFromItem(infoItem));
|
|
jumpToItem(currentIndex());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void QV8ProfilerEventsMainView::selectEventByLocation(const QString &filename, int line)
|
|
{
|
|
if (d->m_preventSelectBounce)
|
|
return;
|
|
|
|
for (int i=0; i<d->m_model->rowCount(); i++) {
|
|
QStandardItem *infoItem = d->m_model->item(i, 0);
|
|
if (currentIndex() != d->m_model->indexFromItem(infoItem) &&
|
|
infoItem->data(FilenameRole).toString() == filename &&
|
|
infoItem->data(LineRole).toInt() == line) {
|
|
setCurrentIndex(d->m_model->indexFromItem(infoItem));
|
|
jumpToItem(currentIndex());
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
QModelIndex QV8ProfilerEventsMainView::selectedItem() const
|
|
{
|
|
QModelIndexList sel = selectedIndexes();
|
|
if (sel.isEmpty())
|
|
return QModelIndex();
|
|
else
|
|
return sel.first();
|
|
}
|
|
|
|
QString QV8ProfilerEventsMainView::QV8ProfilerEventsMainViewPrivate::textForItem(QStandardItem *item, bool recursive = true) const
|
|
{
|
|
QString str;
|
|
|
|
if (recursive) {
|
|
// indentation
|
|
QStandardItem *itemParent = item->parent();
|
|
while (itemParent) {
|
|
str += QLatin1String(" ");
|
|
itemParent = itemParent->parent();
|
|
}
|
|
}
|
|
|
|
// item's data
|
|
int colCount = m_model->columnCount();
|
|
for (int j = 0; j < colCount; ++j) {
|
|
QStandardItem *colItem = item->parent() ? item->parent()->child(item->row(),j) : m_model->item(item->row(),j);
|
|
str += colItem->data(Qt::DisplayRole).toString();
|
|
if (j < colCount-1) str += QLatin1Char('\t');
|
|
}
|
|
str += QLatin1Char('\n');
|
|
|
|
// recursively print children
|
|
if (recursive && item->child(0))
|
|
for (int j = 0; j != item->rowCount(); j++)
|
|
str += textForItem(item->child(j));
|
|
|
|
return str;
|
|
}
|
|
|
|
void QV8ProfilerEventsMainView::copyTableToClipboard() const
|
|
{
|
|
QString str;
|
|
// headers
|
|
int columnCount = d->m_model->columnCount();
|
|
for (int i = 0; i < columnCount; ++i) {
|
|
str += d->m_model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
|
|
if (i < columnCount - 1)
|
|
str += QLatin1Char('\t');
|
|
else
|
|
str += QLatin1Char('\n');
|
|
}
|
|
// data
|
|
int rowCount = d->m_model->rowCount();
|
|
for (int i = 0; i != rowCount; ++i) {
|
|
str += d->textForItem(d->m_model->item(i));
|
|
}
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
clipboard->setText(str, QClipboard::Selection);
|
|
clipboard->setText(str, QClipboard::Clipboard);
|
|
}
|
|
|
|
void QV8ProfilerEventsMainView::copyRowToClipboard() const
|
|
{
|
|
QString str;
|
|
str = d->textForItem(d->m_model->itemFromIndex(selectedItem()), false);
|
|
|
|
QClipboard *clipboard = QApplication::clipboard();
|
|
clipboard->setText(str, QClipboard::Selection);
|
|
clipboard->setText(str, QClipboard::Clipboard);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
QV8ProfilerEventRelativesView::QV8ProfilerEventRelativesView(QV8ProfilerDataModel *model,
|
|
SubViewType viewType,
|
|
QWidget *parent)
|
|
: QmlProfilerTreeView(parent)
|
|
, m_type(viewType)
|
|
, m_v8Model(model)
|
|
, m_model(new QStandardItemModel(this))
|
|
{
|
|
m_model->setSortRole(SortRole);
|
|
setModel(m_model);
|
|
|
|
updateHeader();
|
|
setSortingEnabled(false);
|
|
|
|
connect(this, SIGNAL(activated(QModelIndex)), this, SLOT(jumpToItem(QModelIndex)));
|
|
}
|
|
|
|
QV8ProfilerEventRelativesView::~QV8ProfilerEventRelativesView()
|
|
{
|
|
}
|
|
|
|
void QV8ProfilerEventRelativesView::displayEvent(int index)
|
|
{
|
|
QV8ProfilerDataModel::QV8EventData *event = m_v8Model->v8EventDescription(index);
|
|
QTC_CHECK(event);
|
|
|
|
QList<QV8ProfilerDataModel::QV8EventSub*> events;
|
|
if (m_type == ParentsView)
|
|
events = event->parentHash.values();
|
|
else
|
|
events = event->childrenHash.values();
|
|
|
|
rebuildTree(events);
|
|
|
|
updateHeader();
|
|
resizeColumnToContents(0);
|
|
setSortingEnabled(true);
|
|
sortByColumn(1);
|
|
}
|
|
|
|
void QV8ProfilerEventRelativesView::rebuildTree(QList<QV8ProfilerDataModel::QV8EventSub*> events)
|
|
{
|
|
clear();
|
|
|
|
QStandardItem *topLevelItem = m_model->invisibleRootItem();
|
|
|
|
foreach (QV8ProfilerDataModel::QV8EventSub *event, events) {
|
|
QList<QStandardItem *> newRow;
|
|
newRow << new V8ViewItem(event->reference->displayName);
|
|
newRow << new V8ViewItem(QmlProfilerBaseModel::formatTime(event->totalTime));
|
|
newRow << new V8ViewItem(event->reference->functionName);
|
|
|
|
newRow.at(0)->setData(QVariant(event->reference->eventId), EventIdRole);
|
|
newRow.at(0)->setData(QVariant(event->reference->filename), FilenameRole);
|
|
newRow.at(0)->setData(QVariant(event->reference->line), LineRole);
|
|
newRow.at(1)->setData(QVariant(event->totalTime));
|
|
newRow.at(2)->setData(QVariant(event->reference->functionName));
|
|
|
|
foreach (QStandardItem *item, newRow)
|
|
item->setEditable(false);
|
|
|
|
topLevelItem->appendRow(newRow);
|
|
}
|
|
}
|
|
|
|
void QV8ProfilerEventRelativesView::clear()
|
|
{
|
|
m_model->clear();
|
|
}
|
|
|
|
void QV8ProfilerEventRelativesView::updateHeader()
|
|
{
|
|
m_model->setColumnCount(3);
|
|
|
|
int columnIndex = 0;
|
|
if (m_type == ChildrenView)
|
|
m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(Callee)));
|
|
else
|
|
m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(Caller)));
|
|
|
|
m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(TotalTime)));
|
|
|
|
if (m_type == ChildrenView)
|
|
m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(CalleeDescription)));
|
|
else
|
|
m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(CallerDescription)));
|
|
}
|
|
|
|
void QV8ProfilerEventRelativesView::jumpToItem(const QModelIndex &index)
|
|
{
|
|
QStandardItem *infoItem = m_model->item(index.row(), 0);
|
|
emit eventClicked(infoItem->data(EventIdRole).toInt());
|
|
}
|
|
|
|
} // namespace Internal
|
|
} // namespace QmlProfiler
|