aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/qmlprofiler/qmlprofilereventview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/qmlprofiler/qmlprofilereventview.cpp')
-rw-r--r--plugins/qmlprofiler/qmlprofilereventview.cpp922
1 files changed, 922 insertions, 0 deletions
diff --git a/plugins/qmlprofiler/qmlprofilereventview.cpp b/plugins/qmlprofiler/qmlprofilereventview.cpp
new file mode 100644
index 0000000000..0a13ce7748
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilereventview.cpp
@@ -0,0 +1,922 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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 "qmlprofilereventview.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 <QMenu>
+
+#include <utils/qtcassert.h>
+
+using namespace QmlDebug;
+
+namespace QmlProfiler {
+namespace Internal {
+
+struct Colors {
+ Colors () {
+ this->bindingLoopBackground = QColor("orange").lighter();
+ }
+
+ QColor bindingLoopBackground;
+};
+
+Q_GLOBAL_STATIC(Colors, colors)
+
+////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class EventsViewItem : public QStandardItem
+{
+public:
+ EventsViewItem(const QString &text) : QStandardItem(text) {}
+
+ virtual bool operator<(const QStandardItem &other) const
+ {
+ if (data().type() == QVariant::String) {
+ // first column
+ if (column() == 0) {
+ return data(FilenameRole).toString() == other.data(FilenameRole).toString() ?
+ data(LineRole).toInt() < other.data(LineRole).toInt() :
+ data(FilenameRole).toString() < other.data(FilenameRole).toString();
+ } else {
+ return data().toString().toLower() < other.data().toString().toLower();
+ }
+ }
+
+ return data().toDouble() < other.data().toDouble();
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class QmlProfilerEventsWidget::QmlProfilerEventsWidgetPrivate
+{
+public:
+ QmlProfilerEventsWidgetPrivate(QmlProfilerEventsWidget *qq):q(qq) {}
+ ~QmlProfilerEventsWidgetPrivate() {}
+
+ QmlProfilerEventsWidget *q;
+
+ Analyzer::IAnalyzerTool *m_profilerTool;
+ QmlProfilerViewManager *m_viewContainer;
+
+ QmlProfilerEventsMainView *m_eventTree;
+ QmlProfilerEventRelativesView *m_eventChildren;
+ QmlProfilerEventRelativesView *m_eventParents;
+
+ QmlProfilerEventsModelProxy *modelProxy;
+ bool globalStats;
+};
+
+QmlProfilerEventsWidget::QmlProfilerEventsWidget(QWidget *parent,
+ Analyzer::IAnalyzerTool *profilerTool,
+ QmlProfilerViewManager *container,
+ QmlProfilerModelManager *profilerModelManager )
+ : QWidget(parent), d(new QmlProfilerEventsWidgetPrivate(this))
+{
+ setObjectName(QLatin1String("QmlProfilerEventsView"));
+
+ d->modelProxy = new QmlProfilerEventsModelProxy(profilerModelManager, this);
+ connect(profilerModelManager, SIGNAL(stateChanged()),
+ this, SLOT(profilerDataModelStateChanged()));
+
+ d->m_eventTree = new QmlProfilerEventsMainView(this, d->modelProxy);
+ connect(d->m_eventTree, SIGNAL(gotoSourceLocation(QString,int,int)), this, SIGNAL(gotoSourceLocation(QString,int,int)));
+ connect(d->m_eventTree, SIGNAL(showEventInTimeline(int)), this, SIGNAL(showEventInTimeline(int)));
+
+ d->m_eventChildren = new QmlProfilerEventRelativesView(
+ profilerModelManager,
+ new QmlProfilerEventChildrenModelProxy(profilerModelManager, d->modelProxy, this),
+ this);
+ d->m_eventParents = new QmlProfilerEventRelativesView(
+ profilerModelManager,
+ new QmlProfilerEventParentsModelProxy(profilerModelManager, d->modelProxy, this),
+ this);
+ connect(d->m_eventTree, SIGNAL(eventSelected(QString)), d->m_eventChildren, SLOT(displayEvent(QString)));
+ connect(d->m_eventTree, SIGNAL(eventSelected(QString)), d->m_eventParents, SLOT(displayEvent(QString)));
+ connect(d->m_eventChildren, SIGNAL(eventClicked(QString)), d->m_eventTree, SLOT(selectEvent(QString)));
+ connect(d->m_eventParents, SIGNAL(eventClicked(QString)), d->m_eventTree, SLOT(selectEvent(QString)));
+
+ // 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;
+ d->globalStats = true;
+}
+
+QmlProfilerEventsWidget::~QmlProfilerEventsWidget()
+{
+ delete d->modelProxy;
+ delete d;
+}
+
+void QmlProfilerEventsWidget::profilerDataModelStateChanged()
+{
+}
+
+void QmlProfilerEventsWidget::clear()
+{
+ d->m_eventTree->clear();
+ d->m_eventChildren->clear();
+ d->m_eventParents->clear();
+}
+
+void QmlProfilerEventsWidget::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
+{
+ d->modelProxy->limitToRange(rangeStart, rangeEnd);
+ d->globalStats = (rangeStart == -1) && (rangeEnd == -1);
+}
+
+QModelIndex QmlProfilerEventsWidget::selectedItem() const
+{
+ return d->m_eventTree->selectedItem();
+}
+
+void QmlProfilerEventsWidget::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QTC_ASSERT(d->m_viewContainer, return;);
+
+ QMenu menu;
+ QAction *copyRowAction = 0;
+ QAction *copyTableAction = 0;
+ QAction *showExtendedStatsAction = 0;
+ QAction *getLocalStatsAction = 0;
+ QAction *getGlobalStatsAction = 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(tr("Copy Row"));
+ copyTableAction = menu.addAction(tr("Copy Table"));
+
+ showExtendedStatsAction = menu.addAction(tr("Extended Event Statistics"));
+ showExtendedStatsAction->setCheckable(true);
+ showExtendedStatsAction->setChecked(showExtendedStatistics());
+ }
+
+ menu.addSeparator();
+ getLocalStatsAction = menu.addAction(tr("Limit Events Pane to Current Range"));
+ if (!d->m_viewContainer->hasValidSelection())
+ getLocalStatsAction->setEnabled(false);
+ getGlobalStatsAction = menu.addAction(tr("Reset Events Pane"));
+ if (hasGlobalStats())
+ getGlobalStatsAction->setEnabled(false);
+
+ QAction *selectedAction = menu.exec(position);
+
+ if (selectedAction) {
+ if (selectedAction == copyRowAction)
+ copyRowToClipboard();
+ if (selectedAction == copyTableAction)
+ copyTableToClipboard();
+ if (selectedAction == getLocalStatsAction) {
+ getStatisticsInRange(d->m_viewContainer->selectionStart(),
+ d->m_viewContainer->selectionEnd());
+ }
+ if (selectedAction == getGlobalStatsAction)
+ getStatisticsInRange(-1, -1);
+ if (selectedAction == showExtendedStatsAction)
+ setShowExtendedStatistics(!showExtendedStatistics());
+ }
+}
+
+void QmlProfilerEventsWidget::resizeEvent(QResizeEvent *event)
+{
+ QWidget::resizeEvent(event);
+ emit resized();
+}
+
+bool QmlProfilerEventsWidget::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 QmlProfilerEventsWidget::copyTableToClipboard() const
+{
+ d->m_eventTree->copyTableToClipboard();
+}
+
+void QmlProfilerEventsWidget::copyRowToClipboard() const
+{
+ d->m_eventTree->copyRowToClipboard();
+}
+
+void QmlProfilerEventsWidget::updateSelectedEvent(const QString &eventHash) const
+{
+ if (d->m_eventTree->selectedEventHash() != eventHash)
+ d->m_eventTree->selectEvent(eventHash);
+}
+
+void QmlProfilerEventsWidget::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);
+}
+
+bool QmlProfilerEventsWidget::hasGlobalStats() const
+{
+ return d->globalStats;
+}
+
+void QmlProfilerEventsWidget::setShowExtendedStatistics(bool show)
+{
+ d->m_eventTree->setShowExtendedStatistics(show);
+}
+
+bool QmlProfilerEventsWidget::showExtendedStatistics() const
+{
+ return d->m_eventTree->showExtendedStatistics();
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate
+{
+public:
+ QmlProfilerEventsMainViewPrivate(QmlProfilerEventsMainView *qq) : q(qq) {}
+
+ int getFieldCount();
+
+ QString textForItem(QStandardItem *item, bool recursive = false) const;
+
+
+ QmlProfilerEventsMainView *q;
+
+ QmlProfilerEventsModelProxy *modelProxy;
+ QStandardItemModel *m_model;
+ QList<bool> m_fieldShown;
+ QHash<int, int> m_columnIndex; // maps field enum to column index
+ bool m_showExtendedStatistics;
+ int m_firstNumericColumn;
+ bool m_preventSelectBounce;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+QmlProfilerEventsMainView::QmlProfilerEventsMainView(QWidget *parent,
+ QmlProfilerEventsModelProxy *modelProxy)
+: QmlProfilerTreeView(parent), d(new QmlProfilerEventsMainViewPrivate(this))
+{
+ setObjectName(QLatin1String("QmlProfilerEventsTable"));
+
+ setSortingEnabled(false);
+
+ d->m_model = new QStandardItemModel(this);
+ setModel(d->m_model);
+ connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
+
+ d->modelProxy = modelProxy;
+ connect(d->modelProxy,SIGNAL(dataAvailable()), this, SLOT(buildModel()));
+// connect(d->modelProxy,SIGNAL(stateChanged()),
+// this,SLOT(profilerDataModelStateChanged()));
+ d->m_firstNumericColumn = 0;
+ d->m_preventSelectBounce = false;
+ d->m_showExtendedStatistics = false;
+
+ setFieldViewable(Name, true);
+ setFieldViewable(Type, true);
+ setFieldViewable(Percent, true);
+ setFieldViewable(TotalDuration, true);
+ setFieldViewable(SelfPercent, false);
+ setFieldViewable(SelfDuration, false);
+ setFieldViewable(CallCount, true);
+ setFieldViewable(TimePerCall, true);
+ setFieldViewable(MaxTime, true);
+ setFieldViewable(MinTime, true);
+ setFieldViewable(MedianTime, true);
+ setFieldViewable(Details, true);
+
+ buildModel();
+}
+
+QmlProfilerEventsMainView::~QmlProfilerEventsMainView()
+{
+ clear();
+ //delete d->modelProxy;
+ delete d->m_model;
+ delete d;
+}
+
+void QmlProfilerEventsMainView::profilerDataModelStateChanged()
+{
+}
+
+void QmlProfilerEventsMainView::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 QmlProfilerEventsMainView::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(tr("Location")));
+ d->m_firstNumericColumn++;
+ }
+ if (d->m_fieldShown[Type]) {
+ d->m_columnIndex[Type] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Type")));
+ d->m_firstNumericColumn++;
+ }
+ if (d->m_fieldShown[Percent]) {
+ d->m_columnIndex[Percent] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Time in Percent")));
+ }
+ if (d->m_fieldShown[TotalDuration]) {
+ d->m_columnIndex[TotalDuration] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Total Time")));
+ }
+ if (d->m_fieldShown[SelfPercent]) {
+ d->m_columnIndex[Type] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Self Time in Percent")));
+ }
+ if (d->m_fieldShown[SelfDuration]) {
+ d->m_columnIndex[SelfDuration] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Self Time")));
+ }
+ if (d->m_fieldShown[CallCount]) {
+ d->m_columnIndex[CallCount] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Calls")));
+ }
+ if (d->m_fieldShown[TimePerCall]) {
+ d->m_columnIndex[TimePerCall] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Mean Time")));
+ }
+ if (d->m_fieldShown[MedianTime]) {
+ d->m_columnIndex[MedianTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Median Time")));
+ }
+ if (d->m_fieldShown[MaxTime]) {
+ d->m_columnIndex[MaxTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Longest Time")));
+ }
+ if (d->m_fieldShown[MinTime]) {
+ d->m_columnIndex[MinTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Shortest Time")));
+ }
+ if (d->m_fieldShown[Details]) {
+ d->m_columnIndex[Details] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Details")));
+ }
+}
+
+void QmlProfilerEventsMainView::setShowExtendedStatistics(bool show)
+{
+ // Not checking if already set because we don't want the first call to skip
+ d->m_showExtendedStatistics = show;
+ if (show) {
+ if (d->m_fieldShown[MedianTime])
+ showColumn(d->m_columnIndex[MedianTime]);
+ if (d->m_fieldShown[MaxTime])
+ showColumn(d->m_columnIndex[MaxTime]);
+ if (d->m_fieldShown[MinTime])
+ showColumn(d->m_columnIndex[MinTime]);
+ } else{
+ if (d->m_fieldShown[MedianTime])
+ hideColumn(d->m_columnIndex[MedianTime]);
+ if (d->m_fieldShown[MaxTime])
+ hideColumn(d->m_columnIndex[MaxTime]);
+ if (d->m_fieldShown[MinTime])
+ hideColumn(d->m_columnIndex[MinTime]);
+ }
+}
+
+bool QmlProfilerEventsMainView::showExtendedStatistics() const
+{
+ return d->m_showExtendedStatistics;
+}
+
+void QmlProfilerEventsMainView::clear()
+{
+ d->m_model->clear();
+ d->m_model->setColumnCount(d->getFieldCount());
+
+ setHeaderLabels();
+ setSortingEnabled(false);
+}
+
+int QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::getFieldCount()
+{
+ int count = 0;
+ for (int i=0; i < m_fieldShown.count(); ++i)
+ if (m_fieldShown[i])
+ count++;
+ return count;
+}
+
+void QmlProfilerEventsMainView::buildModel()
+{
+ clear();
+ parseModelProxy();
+ setShowExtendedStatistics(d->m_showExtendedStatistics);
+
+ 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 QmlProfilerEventsMainView::parseModelProxy()
+{
+ const QList <QmlProfilerEventsModelProxy::QmlEventStats> eventList = d->modelProxy->getData();
+ foreach (const QmlProfilerEventsModelProxy::QmlEventStats &event, eventList) {
+ QStandardItem *parentItem = d->m_model->invisibleRootItem();
+ QList<QStandardItem *> newRow;
+
+ if (d->m_fieldShown[Name])
+ newRow << new EventsViewItem(event.displayName);
+
+ if (d->m_fieldShown[Type]) {
+ QString typeString = QmlProfilerEventsMainView::nameForType(event.eventType);
+ QString toolTipText;
+ if (event.eventType == Binding) {
+ if (event.bindingType == (int)OptimizedBinding) {
+ typeString = typeString + tr(" (Opt)");
+ toolTipText = tr("Binding is evaluated by the optimized engine.");
+ } else if (event.bindingType == (int)V8Binding) {
+ toolTipText = tr("Binding not optimized (e.g. has side effects or assignments,\n"
+ "references to elements in other files, loops, etc.)");
+
+ }
+ }
+ newRow << new EventsViewItem(typeString);
+ newRow.last()->setData(QVariant(typeString));
+ if (!toolTipText.isEmpty())
+ newRow.last()->setToolTip(toolTipText);
+ }
+
+ if (d->m_fieldShown[Percent]) {
+ newRow << new EventsViewItem(QString::number(event.percentOfTime,'f',2)+QLatin1String(" %"));
+ newRow.last()->setData(QVariant(event.percentOfTime));
+ }
+
+ if (d->m_fieldShown[TotalDuration]) {
+ newRow << new EventsViewItem(displayTime(event.duration));
+ newRow.last()->setData(QVariant(event.duration));
+ }
+
+ if (d->m_fieldShown[CallCount]) {
+ newRow << new EventsViewItem(QString::number(event.calls));
+ newRow.last()->setData(QVariant(event.calls));
+ }
+
+ if (d->m_fieldShown[TimePerCall]) {
+ newRow << new EventsViewItem(displayTime(event.timePerCall));
+ newRow.last()->setData(QVariant(event.timePerCall));
+ }
+
+ if (d->m_fieldShown[MedianTime]) {
+ newRow << new EventsViewItem(displayTime(event.medianTime));
+ newRow.last()->setData(QVariant(event.medianTime));
+ }
+
+ if (d->m_fieldShown[MaxTime]) {
+ newRow << new EventsViewItem(displayTime(event.maxTime));
+ newRow.last()->setData(QVariant(event.maxTime));
+ }
+
+ if (d->m_fieldShown[MinTime]) {
+ newRow << new EventsViewItem(displayTime(event.minTime));
+ newRow.last()->setData(QVariant(event.minTime));
+ }
+
+ if (d->m_fieldShown[Details]) {
+ newRow << new EventsViewItem(event.details);
+ newRow.last()->setData(QVariant(event.details));
+ }
+
+
+
+ if (!newRow.isEmpty()) {
+ // no edit
+ foreach (QStandardItem *item, newRow)
+ item->setEditable(false);
+
+ // metadata
+ newRow.at(0)->setData(QVariant(event.eventHashStr),EventHashStrRole);
+ newRow.at(0)->setData(QVariant(event.location.filename),FilenameRole);
+ newRow.at(0)->setData(QVariant(event.location.line),LineRole);
+ newRow.at(0)->setData(QVariant(event.location.column),ColumnRole);
+
+ if (event.isBindingLoop) {
+ foreach (QStandardItem *item, newRow) {
+ item->setBackground(colors()->bindingLoopBackground);
+ item->setToolTip(tr("Binding loop detected."));
+ }
+ }
+
+ // append
+ parentItem->appendRow(newRow);
+ }
+ }
+}
+
+QString QmlProfilerEventsMainView::displayTime(double time)
+{
+ if (time < 1e6)
+ return QString::number(time/1e3,'f',3) + trUtf8(" \xc2\xb5s");
+ if (time < 1e9)
+ return QString::number(time/1e6,'f',3) + tr(" ms");
+
+ return QString::number(time/1e9,'f',3) + tr(" s");
+}
+
+QString QmlProfilerEventsMainView::nameForType(int typeNumber)
+{
+ switch (typeNumber) {
+ case 0: return QmlProfilerEventsMainView::tr("Paint");
+ case 1: return QmlProfilerEventsMainView::tr("Compile");
+ case 2: return QmlProfilerEventsMainView::tr("Create");
+ case 3: return QmlProfilerEventsMainView::tr("Binding");
+ case 4: return QmlProfilerEventsMainView::tr("Signal");
+ }
+ return QString();
+}
+
+void QmlProfilerEventsMainView::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
+{
+ d->modelProxy->limitToRange(rangeStart, rangeEnd);
+}
+
+QString QmlProfilerEventsMainView::selectedEventHash() const
+{
+ QModelIndex index = selectedItem();
+ if (!index.isValid())
+ return QString();
+ QStandardItem *item = d->m_model->item(index.row(), 0);
+ return item->data(EventHashStrRole).toString();
+}
+
+
+void QmlProfilerEventsMainView::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();
+ int column = infoItem->data(ColumnRole).toInt();
+ QString fileName = infoItem->data(FilenameRole).toString();
+ if (line!=-1 && !fileName.isEmpty())
+ emit gotoSourceLocation(fileName, line, column);
+
+ // show in callers/callees subwindow
+ emit eventSelected(infoItem->data(EventHashStrRole).toString());
+
+ // show in timelinerenderer
+ emit showEventInTimeline(infoItem->data(EventIdRole).toInt());
+
+ d->m_preventSelectBounce = false;
+}
+
+void QmlProfilerEventsMainView::selectEvent(const QString &eventHash)
+{
+ for (int i=0; i<d->m_model->rowCount(); i++) {
+ QStandardItem *infoItem = d->m_model->item(i, 0);
+ if (infoItem->data(EventHashStrRole).toString() == eventHash) {
+ setCurrentIndex(d->m_model->indexFromItem(infoItem));
+ jumpToItem(currentIndex());
+ return;
+ }
+ }
+}
+
+void QmlProfilerEventsMainView::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 QmlProfilerEventsMainView::selectedItem() const
+{
+ QModelIndexList sel = selectedIndexes();
+ if (sel.isEmpty())
+ return QModelIndex();
+ else
+ return sel.first();
+}
+
+void QmlProfilerEventsMainView::changeDetailsForEvent(int eventId, const QString &newString)
+{
+ for (int i=0; i<d->m_model->rowCount(); i++) {
+ QStandardItem *infoItem = d->m_model->item(i, 0);
+ if (infoItem->data(EventIdRole).toInt() == eventId) {
+ d->m_model->item(i,d->m_columnIndex[Details])->setData(QVariant(newString),Qt::DisplayRole);
+ d->m_model->item(i,d->m_columnIndex[Details])->setData(QVariant(newString));
+ return;
+ }
+ }
+}
+
+QString QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::textForItem(QStandardItem *item, bool recursive) 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 QmlProfilerEventsMainView::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 QmlProfilerEventsMainView::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);
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class QmlProfilerEventRelativesView::QmlProfilerEventParentsViewPrivate
+{
+public:
+ QmlProfilerEventParentsViewPrivate(QmlProfilerEventRelativesView *qq):q(qq) {}
+ ~QmlProfilerEventParentsViewPrivate() {}
+
+ QmlProfilerEventRelativesModelProxy *modelProxy;
+
+ QmlProfilerEventRelativesView *q;
+};
+
+QmlProfilerEventRelativesView::QmlProfilerEventRelativesView(QmlProfilerModelManager *modelManager, QmlProfilerEventRelativesModelProxy *modelProxy, QWidget *parent)
+ : QmlProfilerTreeView(parent), d(new QmlProfilerEventParentsViewPrivate(this))
+{
+ Q_UNUSED(modelManager);
+ setSortingEnabled(false);
+ d->modelProxy = modelProxy;
+ setModel(new QStandardItemModel(this));
+ setRootIsDecorated(false);
+ updateHeader();
+
+ connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
+}
+
+QmlProfilerEventRelativesView::~QmlProfilerEventRelativesView()
+{
+ delete d;
+}
+
+void QmlProfilerEventRelativesView::displayEvent(const QString &eventHash)
+{
+ rebuildTree(d->modelProxy->getData(eventHash));
+
+ updateHeader();
+ resizeColumnToContents(0);
+ setSortingEnabled(true);
+ sortByColumn(2);
+}
+
+void QmlProfilerEventRelativesView::rebuildTree(QmlProfilerEventRelativesModelProxy::QmlEventRelativesMap eventMap)
+{
+ Q_ASSERT(treeModel());
+ treeModel()->clear();
+
+ QStandardItem *topLevelItem = treeModel()->invisibleRootItem();
+
+ //foreach (const QmlProfilerEventParentsModelProxy::QmlEventParentData &event, eventMap.values()) {
+ foreach (const QString &key, eventMap.keys()) {
+ const QmlProfilerEventRelativesModelProxy::QmlEventRelativesData &event = eventMap[key];
+ QList<QStandardItem *> newRow;
+
+ // ToDo: here we were going to search for the data in the other modelproxy
+ // maybe we should store the data in this proxy and get it here
+ // no indirections at this level of abstraction!
+ newRow << new EventsViewItem(event.displayName);
+ newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event.eventType));
+ newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event.duration));
+ newRow << new EventsViewItem(QString::number(event.calls));
+ newRow << new EventsViewItem(event.details);
+
+// newRow << new EventsViewItem(event->reference->displayName);
+// newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event->reference->eventType));
+// newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event->duration));
+// newRow << new EventsViewItem(QString::number(event->calls));
+// newRow << new EventsViewItem(event->reference->details);
+ newRow.at(0)->setData(QVariant(key), EventHashStrRole);
+// newRow.at(0)->setData(QVariant(event->reference->eventId), EventIdRole);
+ newRow.at(2)->setData(QVariant(event.duration));
+ newRow.at(3)->setData(QVariant(event.calls));
+
+ if (event.isBindingLoop) {
+ foreach (QStandardItem *item, newRow) {
+ item->setBackground(colors()->bindingLoopBackground);
+ item->setToolTip(tr("Part of binding loop."));
+ }
+ }
+
+ foreach (QStandardItem *item, newRow)
+ item->setEditable(false);
+
+ topLevelItem->appendRow(newRow);
+ }
+}
+
+void QmlProfilerEventRelativesView::clear()
+{
+ if (treeModel()) {
+ treeModel()->clear();
+ updateHeader();
+ }
+}
+
+void QmlProfilerEventRelativesView::updateHeader()
+{
+ bool calleesView = qobject_cast<QmlProfilerEventChildrenModelProxy *>(d->modelProxy) != 0;
+
+ if (treeModel()) {
+ treeModel()->setColumnCount(5);
+
+ int columnIndex = 0;
+ if (calleesView)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee")));
+ else
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller")));
+
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Type")));
+
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Total Time")));
+
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Calls")));
+
+
+ if (calleesView)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee Description")));
+ else
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller Description")));
+ }
+}
+
+QStandardItemModel *QmlProfilerEventRelativesView::treeModel()
+{
+ return qobject_cast<QStandardItemModel *>(model());
+}
+
+void QmlProfilerEventRelativesView::jumpToItem(const QModelIndex &index)
+{
+ if (treeModel()) {
+ QStandardItem *infoItem = treeModel()->item(index.row(), 0);
+ emit eventClicked(infoItem->data(EventHashStrRole).toString());
+ }
+}
+
+} // namespace Internal
+} // namespace QmlProfiler