aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorKent Hansen <kent.hansen@nokia.com>2012-03-19 12:29:32 +0100
committerKent Hansen <kent.hansen@nokia.com>2012-03-19 12:37:03 +0100
commit4821058f10118be55a541ad39e25ec9165cca3b3 (patch)
tree167973e3c68fc442b0e37d6f628f3fdbe759f15a /tools
parent83f11e33745180e9370d484cbcedd0bac020c9dd (diff)
parent26d5f2e833f0e3686aaa27e695bbfab5fbd808ad (diff)
Merge master into api_changes
Conflicts: src/qml/debugger/qqmlenginedebugservice.cpp src/qml/qml/v8/qv8qobjectwrapper.cpp src/quick/util/qquickimageprovider.cpp tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp Change-Id: Ie78ba2fabd32f4812bcae9dbdd66ed289dc11dcb
Diffstat (limited to 'tools')
-rw-r--r--tools/easingcurveeditor/Button.qml177
-rw-r--r--tools/easingcurveeditor/easingcurveeditor.pro25
-rw-r--r--tools/easingcurveeditor/main.cpp55
-rw-r--r--tools/easingcurveeditor/mainwindow.cpp136
-rw-r--r--tools/easingcurveeditor/mainwindow.h77
-rw-r--r--tools/easingcurveeditor/pane.ui112
-rw-r--r--tools/easingcurveeditor/preview.qml148
-rw-r--r--tools/easingcurveeditor/properties.ui131
-rw-r--r--tools/easingcurveeditor/resources.qrc6
-rw-r--r--tools/easingcurveeditor/segmentproperties.cpp128
-rw-r--r--tools/easingcurveeditor/segmentproperties.h94
-rw-r--r--tools/easingcurveeditor/splineeditor.cpp714
-rw-r--r--tools/easingcurveeditor/splineeditor.h143
-rw-r--r--tools/qmlprofiler/constants.h11
-rw-r--r--tools/qmlprofiler/profiledata.cpp1902
-rw-r--r--tools/qmlprofiler/profiledata.h247
-rw-r--r--tools/qmlprofiler/qmlprofiler.pro11
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.cpp22
-rw-r--r--tools/qmlprofiler/qmlprofilerapplication.h9
-rw-r--r--tools/qmlprofiler/qmlprofilerclient.cpp (renamed from tools/qmlprofiler/profileclient.cpp)56
-rw-r--r--tools/qmlprofiler/qmlprofilerclient.h (renamed from tools/qmlprofiler/profileclient.h)33
-rw-r--r--tools/qmlprofiler/qmlprofilerdata.cpp602
-rw-r--r--tools/qmlprofiler/qmlprofilerdata.h104
-rw-r--r--tools/qmlprofiler/qmlprofilereventlocation.h61
-rw-r--r--tools/tools.pro2
25 files changed, 2781 insertions, 2225 deletions
diff --git a/tools/easingcurveeditor/Button.qml b/tools/easingcurveeditor/Button.qml
new file mode 100644
index 0000000000..a47809a2d1
--- /dev/null
+++ b/tools/easingcurveeditor/Button.qml
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ id: button
+
+ signal clicked
+
+ Rectangle {
+ id: normalBackground
+ radius: 4
+ anchors.fill: parent
+ smooth: true
+ gradient: Gradient {
+ GradientStop {
+ position: 0
+ color: "#afafaf"
+ }
+
+ GradientStop {
+ position: 0.460
+ color: "#808080"
+ }
+
+ GradientStop {
+ position: 1
+ color: "#adadad"
+ }
+ }
+ border.color: "#000000"
+ }
+
+
+ Rectangle {
+ id: hoveredBackground
+ x: 2
+ y: -8
+ radius: 4
+ opacity: 0
+ gradient: Gradient {
+ GradientStop {
+ position: 0
+ color: "#cacaca"
+ }
+
+ GradientStop {
+ position: 0.460
+ color: "#a2a2a2"
+ }
+
+ GradientStop {
+ position: 1
+ color: "#c8c8c8"
+ }
+ }
+ smooth: true
+ anchors.fill: parent
+ border.color: "#000000"
+ }
+
+
+ Rectangle {
+ id: pressedBackground
+ x: -8
+ y: 2
+ radius: 4
+ opacity: 0
+ gradient: Gradient {
+ GradientStop {
+ position: 0
+ color: "#8b8b8b"
+ }
+
+ GradientStop {
+ position: 0.470
+ color: "#626161"
+ }
+
+ GradientStop {
+ position: 1
+ color: "#8f8e8e"
+ }
+ }
+ smooth: true
+ anchors.fill: parent
+ border.color: "#000000"
+ }
+ states: [
+ State {
+ name: "hovered"
+
+ PropertyChanges {
+ target: normalBackground
+ opacity: 0
+ }
+
+ PropertyChanges {
+ target: hoveredBackground
+ opacity: 1
+ }
+ },
+ State {
+ name: "pressed"
+
+ PropertyChanges {
+ target: normalBackground
+ opacity: 0
+ }
+
+ PropertyChanges {
+ target: pressedBackground
+ opacity: 1
+ }
+ }
+ ]
+
+ Text {
+ color: "#e8e8e8"
+ text: qsTr("Play")
+ anchors.fill: parent
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ font.bold: true
+ font.pixelSize: 20
+ }
+
+ MouseArea {
+ hoverEnabled: true
+ anchors.fill: parent
+ onEntered: button.state = "hovered"
+ onExited: button.state = ""
+ onClicked: {
+ button.state = "pressed"
+ button.clicked();
+ }
+ }
+}
diff --git a/tools/easingcurveeditor/easingcurveeditor.pro b/tools/easingcurveeditor/easingcurveeditor.pro
new file mode 100644
index 0000000000..0a266d0aee
--- /dev/null
+++ b/tools/easingcurveeditor/easingcurveeditor.pro
@@ -0,0 +1,25 @@
+TEMPLATE = app
+DEPENDPATH += .
+INCLUDEPATH += .
+
+QT += declarative quick widgets
+CONFIG -= app_bundle
+
+# Input
+SOURCES += main.cpp \
+ splineeditor.cpp \
+ mainwindow.cpp \
+ segmentproperties.cpp
+
+RESOURCES = $$PWD/resources.qrc
+
+HEADERS += \
+ splineeditor.h \
+ mainwindow.h \
+ ui_properties.h \
+ ui_pane.h \
+ segmentproperties.h
+
+FORMS += \
+ properties.ui \
+ pane.ui
diff --git a/tools/easingcurveeditor/main.cpp b/tools/easingcurveeditor/main.cpp
new file mode 100644
index 0000000000..05e8ace53a
--- /dev/null
+++ b/tools/easingcurveeditor/main.cpp
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QApplication>
+#include <mainwindow.h>
+
+
+int main(int argc, char ** argv)
+{
+ QApplication app(argc, argv);
+
+ MainWindow mainWindow;
+ mainWindow.show();
+ mainWindow.showQuickView();
+
+ return app.exec();
+}
diff --git a/tools/easingcurveeditor/mainwindow.cpp b/tools/easingcurveeditor/mainwindow.cpp
new file mode 100644
index 0000000000..569b74ae29
--- /dev/null
+++ b/tools/easingcurveeditor/mainwindow.cpp
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "mainwindow.h"
+#include "splineeditor.h"
+#include <QtQuick/QQuickView>
+#include <QtQuick>
+#include <QtQml/QQmlContext>
+#include <QEasingCurve>
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+
+MainWindow::MainWindow(QWidget *parent) :
+ QMainWindow(parent)
+{
+ SplineEditor *splineEditor = new SplineEditor(this);
+
+ QWidget *mainWidget = new QWidget(this);
+
+ setCentralWidget(mainWidget);
+
+ QHBoxLayout *hboxLayout = new QHBoxLayout(mainWidget);
+ QVBoxLayout *vboxLayout = new QVBoxLayout();
+
+ mainWidget->setLayout(hboxLayout);
+ hboxLayout->addLayout(vboxLayout);
+
+ QWidget *propertyWidget = new QWidget(this);
+ ui_properties.setupUi(propertyWidget);
+
+ ui_properties.spinBox->setMinimum(50);
+ ui_properties.spinBox->setMaximum(10000);
+ ui_properties.spinBox->setValue(500);
+
+ hboxLayout->addWidget(propertyWidget);
+
+ m_placeholder = new QWidget(this);
+
+ m_placeholder->setFixedSize(quickView.size());
+
+ vboxLayout->addWidget(splineEditor);
+ vboxLayout->addWidget(m_placeholder);
+
+ ui_properties.plainTextEdit->setPlainText(splineEditor->generateCode());
+ connect(splineEditor, SIGNAL(easingCurveCodeChanged(QString)), ui_properties.plainTextEdit, SLOT(setPlainText(QString)));
+
+ quickView.rootContext()->setContextProperty(QLatin1String("spinBox"), ui_properties.spinBox);
+
+ foreach (const QString &name, splineEditor->presetNames())
+ ui_properties.comboBox->addItem(name);
+
+ connect(ui_properties.comboBox, SIGNAL(currentIndexChanged(QString)), splineEditor, SLOT(setPreset(QString)));
+
+ splineEditor->setPreset(ui_properties.comboBox->currentText());
+
+ QVBoxLayout *groupBoxLayout = new QVBoxLayout(ui_properties.groupBox);
+ groupBoxLayout->setMargin(0);
+ ui_properties.groupBox->setLayout(groupBoxLayout);
+
+ groupBoxLayout->addWidget(splineEditor->pointListWidget());
+ m_splineEditor = splineEditor;
+ connect(ui_properties.plainTextEdit, SIGNAL(textChanged()), this, SLOT(textEditTextChanged()));
+ initQml();
+}
+
+void MainWindow::showQuickView()
+{
+ const int margin = 16;
+ quickView.move(pos() + QPoint(0, frameGeometry().height() + margin));
+
+ quickView.raise();
+ quickView.show();
+}
+
+void MainWindow::textEditTextChanged()
+{
+ m_splineEditor->setEasingCurve(ui_properties.plainTextEdit->toPlainText().trimmed());
+}
+
+void MainWindow::moveEvent(QMoveEvent *event)
+{
+ QMainWindow::moveEvent(event);
+ showQuickView();
+}
+
+void MainWindow::resizeEvent(QResizeEvent *event)
+{
+ QMainWindow::resizeEvent(event);
+ showQuickView();
+}
+
+void MainWindow::initQml()
+{
+ quickView.setWindowFlags(Qt::FramelessWindowHint);
+ quickView.rootContext()->setContextProperty(QLatin1String("editor"), m_splineEditor);
+ quickView.setSource(QUrl("qrc:/preview.qml"));
+ quickView.show();
+}
diff --git a/tools/easingcurveeditor/mainwindow.h b/tools/easingcurveeditor/mainwindow.h
new file mode 100644
index 0000000000..44591d33cc
--- /dev/null
+++ b/tools/easingcurveeditor/mainwindow.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QtQuick/QQuickView>
+#include "ui_properties.h"
+
+class SplineEditor;
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+public:
+ explicit MainWindow(QWidget *parent = 0);
+
+ void showQuickView();
+
+signals:
+
+public slots:
+ void textEditTextChanged();
+
+protected:
+ virtual void moveEvent(QMoveEvent *event);
+ virtual void resizeEvent(QResizeEvent *event);
+ void initQml();
+
+private:
+ QQuickView quickView;
+ QWidget *m_placeholder;
+ Ui_Properties ui_properties;
+ SplineEditor *m_splineEditor;
+
+};
+
+#endif // MAINWINDOW_H
diff --git a/tools/easingcurveeditor/pane.ui b/tools/easingcurveeditor/pane.ui
new file mode 100644
index 0000000000..1500589192
--- /dev/null
+++ b/tools/easingcurveeditor/pane.ui
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Pane</class>
+ <widget class="QWidget" name="Pane">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>416</width>
+ <height>47</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>p1</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QWidget" name="widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <property name="margin">
+ <number>2</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_x">
+ <property name="text">
+ <string>x</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDoubleSpinBox" name="p1_x">
+ <property name="decimals">
+ <number>4</number>
+ </property>
+ <property name="singleStep">
+ <double>0.010000000000000</double>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_y">
+ <property name="text">
+ <string>y</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDoubleSpinBox" name="p1_y">
+ <property name="decimals">
+ <number>4</number>
+ </property>
+ <property name="minimum">
+ <double>-10.000000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>10.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.010000000000000</double>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QCheckBox" name="smooth">
+ <property name="text">
+ <string>smooth</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3" alignment="Qt::AlignVCenter">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>99</width>
+ <height>10</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tools/easingcurveeditor/preview.qml b/tools/easingcurveeditor/preview.qml
new file mode 100644
index 0000000000..61f4862e76
--- /dev/null
+++ b/tools/easingcurveeditor/preview.qml
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ id: root
+ width: 800
+ height: 100
+
+ Rectangle {
+ gradient: Gradient {
+ GradientStop {
+ position: 0
+ color: "#aaa7a7"
+ }
+
+ GradientStop {
+ position: 0.340
+ color: "#a4a4a4"
+ }
+
+ GradientStop {
+ position: 1
+ color: "#6b6b6b"
+ }
+ }
+ anchors.fill: parent
+ }
+
+ Button {
+ id: button
+
+ x: 19
+ y: 20
+ width: 133
+ height: 61
+ onClicked: {
+ if (root.state ==="")
+ root.state = "moved";
+ else
+ root.state = "";
+ }
+ }
+
+ Rectangle {
+ id: groove
+ x: 163
+ y: 20
+ width: 622
+ height: 61
+ color: "#919191"
+ radius: 4
+ border.color: "#adadad"
+
+ Rectangle {
+ id: rectangle
+ x: 9
+ y: 9
+ width: 46
+ height: 46
+ color: "#3045b7"
+ radius: 4
+ border.width: 2
+ smooth: true
+ border.color: "#9ea0bb"
+ anchors.bottomMargin: 6
+ anchors.topMargin: 9
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ }
+ }
+ states: [
+ State {
+ name: "moved"
+
+ PropertyChanges {
+ target: rectangle
+ x: 567
+ y: 9
+ anchors.bottomMargin: 6
+ anchors.topMargin: 9
+ }
+ }
+ ]
+
+ transitions: [
+ Transition {
+ from: ""
+ to: "moved"
+ SequentialAnimation {
+ PropertyAnimation {
+ easing: editor.easingCurve
+ property: "x"
+ duration: spinBox.value
+ }
+ }
+ },
+ Transition {
+ from: "moved"
+ to: ""
+ PropertyAnimation {
+ easing: editor.easingCurve
+ property: "x"
+ duration: spinBox.value
+ }
+
+ }
+ ]
+}
diff --git a/tools/easingcurveeditor/properties.ui b/tools/easingcurveeditor/properties.ui
new file mode 100644
index 0000000000..af96e9c7a1
--- /dev/null
+++ b/tools/easingcurveeditor/properties.ui
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Properties</class>
+ <widget class="QWidget" name="Properties">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>487</width>
+ <height>627</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="2">
+ <spacer name="spacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>13</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Duration</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" colspan="2">
+ <widget class="QSpinBox" name="spinBox"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Code</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" colspan="2">
+ <widget class="QPlainTextEdit" name="plainTextEdit">
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>128</height>
+ </size>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ <property name="plainText">
+ <string notr="true"/>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Presets</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" colspan="2">
+ <widget class="QComboBox" name="comboBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <spacer name="spacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="5" column="0" colspan="3">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>400</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Control Points</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/tools/easingcurveeditor/resources.qrc b/tools/easingcurveeditor/resources.qrc
new file mode 100644
index 0000000000..c184af4662
--- /dev/null
+++ b/tools/easingcurveeditor/resources.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/">
+ <file>preview.qml</file>
+ <file>Button.qml</file>
+ </qresource>
+</RCC>
diff --git a/tools/easingcurveeditor/segmentproperties.cpp b/tools/easingcurveeditor/segmentproperties.cpp
new file mode 100644
index 0000000000..886d4636aa
--- /dev/null
+++ b/tools/easingcurveeditor/segmentproperties.cpp
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "segmentproperties.h"
+#include "splineeditor.h"
+
+SegmentProperties::SegmentProperties(QWidget *parent) :
+ QWidget(parent), m_splineEditor(0), m_blockSignals(false)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ layout->setMargin(0);
+ layout->setSpacing(2);
+ setLayout(layout);
+ {
+ QWidget *widget = new QWidget(this);
+ m_ui_pane_c1.setupUi(widget);
+ m_ui_pane_c1.label->setText("c1");
+ m_ui_pane_c1.smooth->setVisible(false);
+ layout->addWidget(widget);
+
+ connect(m_ui_pane_c1.p1_x, SIGNAL(valueChanged(double)), this, SLOT(c1Updated()));
+ connect(m_ui_pane_c1.p1_y, SIGNAL(valueChanged(double)), this, SLOT(c1Updated()));
+ }
+ {
+ QWidget *widget = new QWidget(this);
+ m_ui_pane_c2.setupUi(widget);
+ m_ui_pane_c2.label->setText("c2");
+ m_ui_pane_c2.smooth->setVisible(false);
+ layout->addWidget(widget);
+
+ connect(m_ui_pane_c2.p1_x, SIGNAL(valueChanged(double)), this, SLOT(c2Updated()));
+ connect(m_ui_pane_c2.p1_y, SIGNAL(valueChanged(double)), this, SLOT(c2Updated()));
+ }
+ {
+ QWidget *widget = new QWidget(this);
+ m_ui_pane_p.setupUi(widget);
+ m_ui_pane_p.label->setText("p1");
+ layout->addWidget(widget);
+
+ connect(m_ui_pane_p.smooth, SIGNAL(toggled(bool)), this, SLOT(pUpdated()));
+ connect(m_ui_pane_p.p1_x, SIGNAL(valueChanged(double)), this, SLOT(pUpdated()));
+ connect(m_ui_pane_p.p1_y, SIGNAL(valueChanged(double)), this, SLOT(pUpdated()));
+ }
+}
+
+void SegmentProperties::c1Updated()
+{
+ if (m_splineEditor && !m_blockSignals) {
+ QPointF c1(m_ui_pane_c1.p1_x->value(), m_ui_pane_c1.p1_y->value());
+ m_splineEditor->setControlPoint(m_segment * 3, c1);
+ }
+}
+
+void SegmentProperties::c2Updated()
+{
+ if (m_splineEditor && !m_blockSignals) {
+ QPointF c2(m_ui_pane_c2.p1_x->value(), m_ui_pane_c2.p1_y->value());
+ m_splineEditor->setControlPoint(m_segment * 3 + 1, c2);
+ }
+}
+
+void SegmentProperties::pUpdated()
+{
+ if (m_splineEditor && !m_blockSignals) {
+ QPointF p(m_ui_pane_p.p1_x->value(), m_ui_pane_p.p1_y->value());
+ bool smooth = m_ui_pane_p.smooth->isChecked();
+ m_splineEditor->setControlPoint(m_segment * 3 + 2, p);
+ m_splineEditor->setSmooth(m_segment, smooth);
+ }
+}
+
+void SegmentProperties::invalidate()
+{
+ m_blockSignals = true;
+
+ m_ui_pane_p.label->setText(QLatin1String("p") + QString::number(m_segment));
+ m_ui_pane_p.smooth->setChecked(m_smooth);
+ m_ui_pane_p.smooth->parentWidget()->setEnabled(!m_last);
+
+ m_ui_pane_c1.p1_x->setValue(m_points.at(0).x());
+ m_ui_pane_c1.p1_y->setValue(m_points.at(0).y());
+
+ m_ui_pane_c2.p1_x->setValue(m_points.at(1).x());
+ m_ui_pane_c2.p1_y->setValue(m_points.at(1).y());
+
+ m_ui_pane_p.p1_x->setValue(m_points.at(2).x());
+ m_ui_pane_p.p1_y->setValue(m_points.at(2).y());
+
+ m_blockSignals = false;
+}
diff --git a/tools/easingcurveeditor/segmentproperties.h b/tools/easingcurveeditor/segmentproperties.h
new file mode 100644
index 0000000000..4d86be3882
--- /dev/null
+++ b/tools/easingcurveeditor/segmentproperties.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SEGMENTPROPERTIES_H
+#define SEGMENTPROPERTIES_H
+
+#include <QWidget>
+#include <ui_pane.h>
+
+class SplineEditor;
+
+class SegmentProperties : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit SegmentProperties(QWidget *parent = 0);
+ void setSplineEditor(SplineEditor *splineEditor)
+ {
+ m_splineEditor = splineEditor;
+ }
+
+ void setSegment(int segment, QVector<QPointF> points, bool smooth, bool last)
+ {
+ m_segment = segment;
+ m_points = points;
+ m_smooth = smooth;
+ m_last = last;
+ invalidate();
+ }
+
+signals:
+
+public slots:
+
+private slots:
+ void c1Updated();
+ void c2Updated();
+ void pUpdated();
+
+private:
+ void invalidate();
+
+ Ui_Pane m_ui_pane_c1;
+ Ui_Pane m_ui_pane_c2;
+ Ui_Pane m_ui_pane_p;
+
+ SplineEditor *m_splineEditor;
+ QVector<QPointF> m_points;
+ int m_segment;
+ bool m_smooth;
+ bool m_last;
+
+ bool m_blockSignals;
+};
+
+#endif // SEGMENTPROPERTIES_H
diff --git a/tools/easingcurveeditor/splineeditor.cpp b/tools/easingcurveeditor/splineeditor.cpp
new file mode 100644
index 0000000000..dbd32dd63a
--- /dev/null
+++ b/tools/easingcurveeditor/splineeditor.cpp
@@ -0,0 +1,714 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "splineeditor.h"
+
+#include <QPainter>
+#include <QMouseEvent>
+#include <QContextMenuEvent>
+#include <QDebug>
+#include <QApplication>
+#include <segmentproperties.h>
+
+const int canvasWidth = 640;
+const int canvasHeight = 320;
+
+const int canvasMargin = 160;
+
+SplineEditor::SplineEditor(QWidget *parent) :
+ QWidget(parent), m_pointListWidget(0), m_block(false)
+{
+ setFixedSize(canvasWidth + canvasMargin * 2, canvasHeight + canvasMargin * 2);
+
+ m_controlPoints.append(QPointF(0.4, 0.075));
+ m_controlPoints.append(QPointF(0.45,0.24));
+ m_controlPoints.append(QPointF(0.5,0.5));
+
+ m_controlPoints.append(QPointF(0.55,0.76));
+ m_controlPoints.append(QPointF(0.7,0.9));
+ m_controlPoints.append(QPointF(1.0, 1.0));
+
+ m_numberOfSegments = 2;
+
+ m_activeControlPoint = -1;
+
+ m_mouseDrag = false;
+
+ m_pointContextMenu = new QMenu(this);
+ m_deleteAction = new QAction(tr("Delete point"), m_pointContextMenu);
+ m_smoothAction = new QAction(tr("Smooth point"), m_pointContextMenu);
+ m_cornerAction = new QAction(tr("Corner point"), m_pointContextMenu);
+
+ m_smoothAction->setCheckable(true);
+
+ m_pointContextMenu->addAction(m_deleteAction);
+ m_pointContextMenu->addAction(m_smoothAction);
+ m_pointContextMenu->addAction(m_cornerAction);
+
+ m_curveContextMenu = new QMenu(this);
+
+ m_addPoint = new QAction(tr("Add point"), m_pointContextMenu);
+
+ m_curveContextMenu->addAction(m_addPoint);
+
+ initPresets();
+
+ invalidateSmoothList();
+}
+
+static inline QPointF mapToCanvas(const QPointF &point)
+{
+ return QPointF(point.x() * canvasWidth + canvasMargin,
+ canvasHeight - point.y() * canvasHeight + canvasMargin);
+}
+
+static inline QPointF mapFromCanvas(const QPointF &point)
+{
+ return QPointF((point.x() - canvasMargin) / canvasWidth ,
+ 1 - (point.y() - canvasMargin) / canvasHeight);
+}
+
+static inline void paintControlPoint(const QPointF &controlPoint, QPainter *painter, bool edit,
+ bool realPoint, bool active, bool smooth)
+{
+ int pointSize = 4;
+
+ if (active)
+ painter->setBrush(QColor(140, 140, 240, 255));
+ else
+ painter->setBrush(QColor(120, 120, 220, 255));
+
+ if (realPoint) {
+ pointSize = 6;
+ painter->setBrush(QColor(80, 80, 210, 150));
+ }
+
+ painter->setPen(QColor(50, 50, 50, 140));
+
+ if (!edit)
+ painter->setBrush(QColor(160, 80, 80, 250));
+
+ if (smooth) {
+ painter->drawEllipse(QRectF(mapToCanvas(controlPoint).x() - pointSize + 0.5,
+ mapToCanvas(controlPoint).y() - pointSize + 0.5,
+ pointSize * 2, pointSize * 2));
+ } else {
+ painter->drawRect(QRectF(mapToCanvas(controlPoint).x() - pointSize + 0.5,
+ mapToCanvas(controlPoint).y() - pointSize + 0.5,
+ pointSize * 2, pointSize * 2));
+ }
+}
+
+static inline bool indexIsRealPoint(int i)
+{
+ return !((i + 1) % 3);
+}
+
+static inline int pointForControlPoint(int i)
+{
+ if ((i % 3) == 0)
+ return i - 1;
+
+ if ((i % 3) == 1)
+ return i + 1;
+
+ return i;
+}
+
+void drawCleanLine(QPainter *painter, const QPoint p1, QPoint p2)
+{
+ painter->drawLine(p1 + QPointF(0.5 , 0.5), p2 + QPointF(0.5, 0.5));
+}
+
+void SplineEditor::paintEvent(QPaintEvent *)
+{
+ QPainter painter(this);
+
+ QPen pen(Qt::black);
+ pen.setWidth(1);
+ painter.fillRect(0,0,width() - 1, height() - 1, QBrush(Qt::white));
+ painter.drawRect(0,0,width() - 1, height() - 1);
+
+ painter.setRenderHint(QPainter::Antialiasing);
+
+ pen = QPen(Qt::gray);
+ pen.setWidth(1);
+ pen.setStyle(Qt::DashLine);
+ painter.setPen(pen);
+ drawCleanLine(&painter,mapToCanvas(QPoint(0, 0)).toPoint(), mapToCanvas(QPoint(1, 0)).toPoint());
+ drawCleanLine(&painter,mapToCanvas(QPoint(0, 1)).toPoint(), mapToCanvas(QPoint(1, 1)).toPoint());
+
+ for (int i = 0; i < m_numberOfSegments; i++) {
+ QPainterPath path;
+ QPointF p0;
+
+ if (i == 0)
+ p0 = mapToCanvas(QPointF(0.0, 0.0));
+ else
+ p0 = mapToCanvas(m_controlPoints.at(i * 3 - 1));
+
+ path.moveTo(p0);
+
+ QPointF p1 = mapToCanvas(m_controlPoints.at(i * 3));
+ QPointF p2 = mapToCanvas(m_controlPoints.at(i * 3 + 1));
+ QPointF p3 = mapToCanvas(m_controlPoints.at(i * 3 + 2));
+ path.cubicTo(p1, p2, p3);
+ painter.strokePath(path, QPen(QBrush(Qt::black), 2));
+
+ QPen pen(Qt::black);
+ pen.setWidth(1);
+ pen.setStyle(Qt::DashLine);
+ painter.setPen(pen);
+ painter.drawLine(p0, p1);
+ painter.drawLine(p3, p2);
+ }
+
+ paintControlPoint(QPointF(0.0, 0.0), &painter, false, true, false, false);
+ paintControlPoint(QPointF(1.0, 1.0), &painter, false, true, false, false);
+
+ for (int i = 0; i < m_controlPoints.count() - 1; ++i)
+ paintControlPoint(m_controlPoints.at(i),
+ &painter,
+ true,
+ indexIsRealPoint(i),
+ i == m_activeControlPoint,
+ isControlPointSmooth(i));
+}
+
+void SplineEditor::mousePressEvent(QMouseEvent *e)
+{
+ if (e->button() == Qt::LeftButton) {
+ m_activeControlPoint = findControlPoint(e->pos());
+
+ if (m_activeControlPoint != -1) {
+ mouseMoveEvent(e);
+ }
+ m_mousePress = e->pos();
+ e->accept();
+ }
+}
+
+void SplineEditor::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (e->button() == Qt::LeftButton) {
+ m_activeControlPoint = -1;
+
+ m_mouseDrag = false;
+ e->accept();
+ }
+}
+
+void SplineEditor::contextMenuEvent(QContextMenuEvent *e)
+{
+ int index = findControlPoint(e->pos());
+
+ if (index > 0 && indexIsRealPoint(index)) {
+ m_smoothAction->setChecked(isControlPointSmooth(index));
+ QAction* action = m_pointContextMenu->exec(e->globalPos());
+ if (action == m_deleteAction)
+ deletePoint(index);
+ else if (action == m_smoothAction)
+ smoothPoint(index);
+ else if (action == m_cornerAction)
+ cornerPoint(index);
+ } else {
+ QAction* action = m_curveContextMenu->exec(e->globalPos());
+ if (action == m_addPoint)
+ addPoint(e->pos());
+ }
+}
+
+void SplineEditor::invalidate()
+{
+ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
+
+ for (int i = 0; i < m_numberOfSegments; ++i) {
+ easingCurve.addCubicBezierSegment(m_controlPoints.at(i * 3),
+ m_controlPoints.at(i * 3 + 1),
+ m_controlPoints.at(i * 3 + 2));
+ }
+ setEasingCurve(easingCurve);
+ invalidateSegmentProperties();
+}
+
+void SplineEditor::invalidateSmoothList()
+{
+ m_smoothList.clear();
+
+ for (int i = 0; i < (m_numberOfSegments - 1); ++i)
+ m_smoothList.append(isSmooth(i * 3 + 2));
+
+}
+
+void SplineEditor::invalidateSegmentProperties()
+{
+ for (int i = 0; i < m_numberOfSegments; ++i) {
+ SegmentProperties *segmentProperties = m_segmentProperties.at(i);
+ bool smooth = false;
+ if (i < (m_numberOfSegments - 1)) {
+ smooth = m_smoothList.at(i);
+ }
+ segmentProperties->setSegment(i, m_controlPoints.mid(i * 3, 3), smooth, i == (m_numberOfSegments - 1));
+ }
+}
+
+QHash<QString, QEasingCurve> SplineEditor::presets() const
+{
+ return m_presets;
+}
+
+QString SplineEditor::generateCode()
+{
+ QString s = QLatin1String("[");
+ foreach (const QPointF &point, m_controlPoints) {
+ s += QString::number(point.x(), 'g', 2) + "," + QString::number(point.y(), 'g', 3) + ",";
+ }
+ s.chop(1); //removing last ","
+ s += "]";
+
+ return s;
+}
+
+QStringList SplineEditor::presetNames() const
+{
+ return m_presets.keys();
+}
+
+QWidget *SplineEditor::pointListWidget()
+{
+ if (!m_pointListWidget) {
+ setupPointListWidget();
+ }
+
+ return m_pointListWidget;
+}
+
+int SplineEditor::findControlPoint(const QPoint &point)
+{
+ int pointIndex = -1;
+ qreal distance = -1;
+ for (int i = 0; i<m_controlPoints.size() - 1; ++i) {
+ qreal d = QLineF(point, mapToCanvas(m_controlPoints.at(i))).length();
+ if ((distance < 0 && d < 10) || d < distance) {
+ distance = d;
+ pointIndex = i;
+ }
+ }
+ return pointIndex;
+}
+
+static inline bool veryFuzzyCompare(qreal r1, qreal r2)
+{
+ if (qFuzzyCompare(r1, 2))
+ return true;
+
+ int r1i = qRound(r1 * 20);
+ int r2i = qRound(r2 * 20);
+
+ if (qFuzzyCompare(qreal(r1i) / 20, qreal(r2i) / 20))
+ return true;
+
+ return false;
+}
+
+bool SplineEditor::isSmooth(int i) const
+{
+ if (i == 0)
+ return false;
+
+ QPointF p = m_controlPoints.at(i);
+ QPointF p_before = m_controlPoints.at(i - 1);
+ QPointF p_after = m_controlPoints.at(i + 1);
+
+ QPointF v1 = p_after - p;
+ v1 = v1 / v1.manhattanLength(); //normalize
+
+ QPointF v2 = p - p_before;
+ v2 = v2 / v2.manhattanLength(); //normalize
+
+ return veryFuzzyCompare(v1.x(), v2.x()) && veryFuzzyCompare(v1.y(), v2.y());
+}
+
+void SplineEditor::smoothPoint(int index)
+{
+ if (m_smoothAction->isChecked()) {
+
+ QPointF before = QPointF(0,0);
+ if (index > 3)
+ before = m_controlPoints.at(index - 3);
+
+ QPointF after = QPointF(1.0, 1.0);
+ if ((index + 3) < m_controlPoints.count())
+ after = m_controlPoints.at(index + 3);
+
+ QPointF tangent = (after - before) / 6;
+
+ QPointF thisPoint = m_controlPoints.at(index);
+
+ if (index > 0)
+ m_controlPoints[index - 1] = thisPoint - tangent;
+
+ if (index + 1 < m_controlPoints.count())
+ m_controlPoints[index + 1] = thisPoint + tangent;
+
+ m_smoothList[index / 3] = true;
+ } else {
+ m_smoothList[index / 3] = false;
+ }
+ invalidate();
+ update();
+}
+
+void SplineEditor::cornerPoint(int index)
+{
+ QPointF before = QPointF(0,0);
+ if (index > 3)
+ before = m_controlPoints.at(index - 3);
+
+ QPointF after = QPointF(1.0, 1.0);
+ if ((index + 3) < m_controlPoints.count())
+ after = m_controlPoints.at(index + 3);
+
+ QPointF thisPoint = m_controlPoints.at(index);
+
+ if (index > 0)
+ m_controlPoints[index - 1] = (before - thisPoint) / 3 + thisPoint;
+
+ if (index + 1 < m_controlPoints.count())
+ m_controlPoints[index + 1] = (after - thisPoint) / 3 + thisPoint;
+
+ m_smoothList[(index) / 3] = false;
+ invalidate();
+}
+
+void SplineEditor::deletePoint(int index)
+{
+ m_controlPoints.remove(index - 1, 3);
+ m_numberOfSegments--;
+
+ invalidateSmoothList();
+ setupPointListWidget();
+ invalidate();
+}
+
+void SplineEditor::addPoint(const QPointF point)
+{
+ QPointF newPos = mapFromCanvas(point);
+ int splitIndex = 0;
+ for (int i=0; i < m_controlPoints.size() - 1; ++i) {
+ if (indexIsRealPoint(i) && m_controlPoints.at(i).x() > newPos.x()) {
+ break;
+ } else if (indexIsRealPoint(i))
+ splitIndex = i;
+ }
+ QPointF before = QPointF(0,0);
+ if (splitIndex > 0)
+ before = m_controlPoints.at(splitIndex);
+
+ QPointF after = QPointF(1.0, 1.0);
+ if ((splitIndex + 3) < m_controlPoints.count())
+ after = m_controlPoints.at(splitIndex + 3);
+
+ if (splitIndex > 0) {
+ m_controlPoints.insert(splitIndex + 2, (newPos + after) / 2);
+ m_controlPoints.insert(splitIndex + 2, newPos);
+ m_controlPoints.insert(splitIndex + 2, (newPos + before) / 2);
+ } else {
+ m_controlPoints.insert(splitIndex + 1, (newPos + after) / 2);
+ m_controlPoints.insert(splitIndex + 1, newPos);
+ m_controlPoints.insert(splitIndex + 1, (newPos + before) / 2);
+ }
+ m_numberOfSegments++;
+
+ invalidateSmoothList();
+ setupPointListWidget();
+ invalidate();
+}
+
+void SplineEditor::initPresets()
+{
+ const QPointF endPoint(1.0, 1.0);
+ {
+ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
+ easingCurve.addCubicBezierSegment(QPointF(0.4, 0.075), QPointF(0.45, 0.24), QPointF(0.5, 0.5));
+ easingCurve.addCubicBezierSegment(QPointF(0.55, 0.76), QPointF(0.7, 0.9), endPoint);
+ m_presets.insert(tr("Standard Easing"), easingCurve);
+ }
+
+ {
+ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
+ easingCurve.addCubicBezierSegment(QPointF(0.43, 0.0025), QPointF(0.65, 1), endPoint);
+ m_presets.insert(tr("Simple"), easingCurve);
+ }
+
+ {
+ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
+ easingCurve.addCubicBezierSegment(QPointF(0.43, 0.0025), QPointF(0.38, 0.51), QPointF(0.57, 0.99));
+ easingCurve.addCubicBezierSegment(QPointF(0.8, 0.69), QPointF(0.65, 1), endPoint);
+ m_presets.insert(tr("Simple Bounce"), easingCurve);
+ }
+
+ {
+ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
+ easingCurve.addCubicBezierSegment(QPointF(0.4, 0.075), QPointF(0.64, -0.0025), QPointF(0.74, 0.23));
+ easingCurve.addCubicBezierSegment(QPointF(0.84, 0.46), QPointF(0.91, 0.77), endPoint);
+ m_presets.insert(tr("Slow in fast out"), easingCurve);
+ }
+
+ {
+ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
+ easingCurve.addCubicBezierSegment(QPointF(0.43, 0.0025), QPointF(0.47, 0.51), QPointF(0.59, 0.94));
+ easingCurve.addCubicBezierSegment(QPointF(0.84, 0.95), QPointF( 0.99, 0.94), endPoint);
+ m_presets.insert(tr("Snapping"), easingCurve);
+ }
+
+ {
+ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
+ easingCurve.addCubicBezierSegment(QPointF( 0.38, 0.35),QPointF(0.38, 0.7), QPointF(0.45, 0.99));
+ easingCurve.addCubicBezierSegment(QPointF(0.48, 0.66), QPointF(0.62, 0.62), QPointF(0.66, 0.99));
+ easingCurve.addCubicBezierSegment(QPointF(0.69, 0.76), QPointF(0.77, 0.76), QPointF(0.79, 0.99));
+ easingCurve.addCubicBezierSegment(QPointF(0.83, 0.91), QPointF(0.87, 0.92), QPointF(0.91, 0.99));
+ easingCurve.addCubicBezierSegment(QPointF(0.95, 0.95), QPointF(0.97, 0.94), endPoint);
+ m_presets.insert(tr("Complex Bounce"), easingCurve);
+ }
+
+ {
+ QEasingCurve easingCurve4(QEasingCurve::BezierSpline);
+ easingCurve4.addCubicBezierSegment(QPointF(0.12, -0.12),QPointF(0.23, -0.19), QPointF( 0.35, -0.09));
+ easingCurve4.addCubicBezierSegment(QPointF(0.47, 0.005), QPointF(0.52, 1), QPointF(0.62, 1.1));
+ easingCurve4.addCubicBezierSegment(QPointF(0.73, 1.2), QPointF(0.91,1 ), endPoint);
+ m_presets.insert(tr("Overshoot"), easingCurve4);
+ }
+}
+
+void SplineEditor::setupPointListWidget()
+{
+ if (!m_pointListWidget)
+ m_pointListWidget = new QScrollArea(this);
+
+ if (m_pointListWidget->widget())
+ delete m_pointListWidget->widget();
+
+ m_pointListWidget->setFrameStyle(QFrame::NoFrame);
+ m_pointListWidget->setWidgetResizable(true);
+ m_pointListWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
+ m_pointListWidget->setWidget(new QWidget(m_pointListWidget));
+ QVBoxLayout *layout = new QVBoxLayout(m_pointListWidget->widget());
+ layout->setMargin(0);
+ layout->setSpacing(2);
+ m_pointListWidget->widget()->setLayout(layout);
+
+ m_segmentProperties.clear();
+
+ { //implicit 0,0
+ QWidget *widget = new QWidget(m_pointListWidget->widget());
+ Ui_Pane pane;
+ pane.setupUi(widget);
+ pane.p1_x->setValue(0);
+ pane.p1_y->setValue(0);
+ layout->addWidget(widget);
+ pane.label->setText("p0");
+ widget->setEnabled(false);
+ }
+
+ for (int i = 0; i < m_numberOfSegments; ++i) {
+ SegmentProperties *segmentProperties = new SegmentProperties(m_pointListWidget->widget());
+ layout->addWidget(segmentProperties);
+ bool smooth = false;
+ if (i < (m_numberOfSegments - 1)) {
+ smooth = m_smoothList.at(i);
+ }
+ segmentProperties->setSegment(i, m_controlPoints.mid(i * 3, 3), smooth, i == (m_numberOfSegments - 1));
+ segmentProperties->setSplineEditor(this);
+ m_segmentProperties << segmentProperties;
+ }
+ layout->addSpacerItem(new QSpacerItem(10, 10, QSizePolicy::Expanding, QSizePolicy::Expanding));
+
+ m_pointListWidget->viewport()->show();
+ m_pointListWidget->viewport()->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ m_pointListWidget->show();
+}
+
+bool SplineEditor::isControlPointSmooth(int i) const
+{
+ if (i == 0)
+ return false;
+
+ if (i == m_controlPoints.count() - 1)
+ return false;
+
+ if (m_numberOfSegments == 1)
+ return false;
+
+ int index = pointForControlPoint(i);
+
+ if (index == 0)
+ return false;
+
+ if (index == m_controlPoints.count() - 1)
+ return false;
+
+ return m_smoothList.at(index / 3);
+}
+
+QPointF limitToCanvas(const QPointF point)
+{
+ qreal left = -qreal( canvasMargin) / qreal(canvasWidth);
+ qreal width = 1.0 - 2.0 * left;
+
+ qreal top = -qreal( canvasMargin) / qreal(canvasHeight);
+ qreal height = 1.0 - 2.0 * top;
+
+ QPointF p = point;
+ QRectF r(left, top, width, height);
+
+ if (p.x() > r.right()) {
+ p.setX(r.right());
+ }
+ if (p.x() < r.left()) {
+ p.setX(r.left());
+ }
+ if (p.y() < r.top()) {
+ p.setY(r.top());
+ }
+ if (p.y() > r.bottom()) {
+ p.setY(r.bottom());
+ }
+ return p;
+}
+
+void SplineEditor::mouseMoveEvent(QMouseEvent *e)
+{
+ // If we've moved more then 25 pixels, assume user is dragging
+ if (!m_mouseDrag && QPoint(m_mousePress - e->pos()).manhattanLength() > qApp->startDragDistance())
+ m_mouseDrag = true;
+
+ QPointF p = mapFromCanvas(e->pos());
+
+ if (m_mouseDrag && m_activeControlPoint >= 0 && m_activeControlPoint < m_controlPoints.size()) {
+ p = limitToCanvas(p);
+ if (indexIsRealPoint(m_activeControlPoint)) {
+ //move also the tangents
+ QPointF targetPoint = p;
+ QPointF distance = targetPoint - m_controlPoints[m_activeControlPoint];
+ m_controlPoints[m_activeControlPoint] = targetPoint;
+ m_controlPoints[m_activeControlPoint - 1] += distance;
+ m_controlPoints[m_activeControlPoint + 1] += distance;
+ } else {
+ if (!isControlPointSmooth(m_activeControlPoint)) {
+ m_controlPoints[m_activeControlPoint] = p;
+ } else {
+ QPointF targetPoint = p;
+ QPointF distance = targetPoint - m_controlPoints[m_activeControlPoint];
+ m_controlPoints[m_activeControlPoint] = p;
+
+ if ((m_activeControlPoint > 1) && (m_activeControlPoint % 3) == 0) { //right control point
+ m_controlPoints[m_activeControlPoint - 2] -= distance;
+ } else if ((m_activeControlPoint < (m_controlPoints.count() - 2)) //left control point
+ && (m_activeControlPoint % 3) == 1) {
+ m_controlPoints[m_activeControlPoint + 2] -= distance;
+ }
+ }
+ }
+ invalidate();
+ }
+}
+
+void SplineEditor::setEasingCurve(const QEasingCurve &easingCurve)
+{
+ if (m_easingCurve == easingCurve)
+ return;
+ m_block = true;
+ m_easingCurve = easingCurve;
+ m_controlPoints = m_easingCurve.cubicBezierSpline().toVector();
+ m_numberOfSegments = m_controlPoints.count() / 3;
+ update();
+ emit easingCurveChanged();
+
+ const QString code = generateCode();
+ emit easingCurveCodeChanged(code);
+
+ m_block = false;
+}
+
+void SplineEditor::setPreset(const QString &name)
+{
+ setEasingCurve(m_presets.value(name));
+ invalidateSmoothList();
+ setupPointListWidget();
+}
+
+void SplineEditor::setEasingCurve(const QString &code)
+{
+ if (m_block)
+ return;
+ if (code.left(1) == QLatin1String("[") && code.right(1) == QLatin1String("]")) {
+ QString cleanCode = code;
+ cleanCode.remove(0, 1);
+ cleanCode.chop(1);
+ const QStringList stringList = cleanCode.split(QLatin1Char(','), QString::SkipEmptyParts);
+ if (stringList.count() >= 6 && (stringList.count() % 6 == 0)) {
+ QList<qreal> realList;
+ foreach (const QString &string, stringList) {
+ bool ok;
+ realList.append(string.toDouble(&ok));
+ if (!ok)
+ return;
+ }
+ QList<QPointF> points;
+ for (int i = 0; i < realList.count() / 2; ++i)
+ points.append(QPointF(realList.at(i * 2), realList.at(i * 2 + 1)));
+ if (points.last() == QPointF(1.0, 1.0)) {
+ QEasingCurve easingCurve(QEasingCurve::BezierSpline);
+
+ for (int i = 0; i < points.count() / 3; ++i) {
+ easingCurve.addCubicBezierSegment(points.at(i * 3),
+ points.at(i * 3 + 1),
+ points.at(i * 3 + 2));
+ }
+ setEasingCurve(easingCurve);
+ invalidateSmoothList();
+ setupPointListWidget();
+ }
+ }
+ }
+}
diff --git a/tools/easingcurveeditor/splineeditor.h b/tools/easingcurveeditor/splineeditor.h
new file mode 100644
index 0000000000..81f2dfd1ce
--- /dev/null
+++ b/tools/easingcurveeditor/splineeditor.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SPLINEEDITOR_H
+#define SPLINEEDITOR_H
+
+#include <QWidget>
+#include <QMenu>
+#include <QAction>
+#include <QScrollArea>
+
+#include <QEasingCurve>
+#include <QHash>
+
+class SegmentProperties;
+
+class SplineEditor : public QWidget
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QEasingCurve easingCurve READ easingCurve WRITE setEasingCurve NOTIFY easingCurveChanged);
+
+public:
+ explicit SplineEditor(QWidget *parent = 0);
+ QString generateCode();
+ QStringList presetNames() const;
+ QWidget *pointListWidget();
+
+ void setControlPoint(int index, const QPointF &point)
+ {
+ m_controlPoints[index] = point;
+ update();
+ }
+
+ void setSmooth(int index, bool smooth)
+ {
+ m_smoothAction->setChecked(smooth);
+ smoothPoint(index * 3 + 2);
+ //update();
+ }
+
+signals:
+ void easingCurveChanged();
+ void easingCurveCodeChanged(const QString &code);
+
+
+public slots:
+ void setEasingCurve(const QEasingCurve &easingCurve);
+ void setPreset(const QString &name);
+ void setEasingCurve(const QString &code);
+
+protected:
+ void paintEvent(QPaintEvent *);
+ void mousePressEvent(QMouseEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ void contextMenuEvent(QContextMenuEvent *);
+
+ void invalidate();
+ void invalidateSmoothList();
+ void invalidateSegmentProperties();
+
+ QEasingCurve easingCurve() const
+ { return m_easingCurve; }
+
+ QHash<QString, QEasingCurve> presets() const;
+
+private:
+ int findControlPoint(const QPoint &point);
+ bool isSmooth(int i) const;
+
+ void smoothPoint( int index);
+ void cornerPoint( int index);
+ void deletePoint(int index);
+ void addPoint(const QPointF point);
+
+ void initPresets();
+
+ void setupPointListWidget();
+
+ bool isControlPointSmooth(int i) const;
+
+ QEasingCurve m_easingCurve;
+ QVector<QPointF> m_controlPoints;
+ QVector<bool> m_smoothList;
+ int m_numberOfSegments;
+ int m_activeControlPoint;
+ bool m_mouseDrag;
+ QPoint m_mousePress;
+ QHash<QString, QEasingCurve> m_presets;
+
+ QMenu *m_pointContextMenu;
+ QMenu *m_curveContextMenu;
+ QAction *m_deleteAction;
+ QAction *m_smoothAction;
+ QAction *m_cornerAction;
+ QAction *m_addPoint;
+
+ QScrollArea *m_pointListWidget;
+
+ QList<SegmentProperties*> m_segmentProperties;
+ bool m_block;
+};
+
+#endif // SPLINEEDITOR_H
diff --git a/tools/qmlprofiler/constants.h b/tools/qmlprofiler/constants.h
index e5a1f8025d..d87bad18b1 100644
--- a/tools/qmlprofiler/constants.h
+++ b/tools/qmlprofiler/constants.h
@@ -54,15 +54,6 @@ const char CMD_RECORD2[] ="r";
const char CMD_QUIT[] ="quit";
const char CMD_QUIT2[] = "q";
-const char TYPE_PAINTING_STR[] = "Painting";
-const char TYPE_COMPILING_STR[] = "Compiling";
-const char TYPE_CREATING_STR[] = "Creating";
-const char TYPE_BINDING_STR[] = "Binding";
-const char TYPE_HANDLINGSIGNAL_STR[] = "HandlingSignal";
-const char PROFILER_FILE_VERSION[] = "1.02";
-
-const int MIN_LEVEL = 1;
-
-} // Contants
+} // Constants
#endif // CONSTANTS_H
diff --git a/tools/qmlprofiler/profiledata.cpp b/tools/qmlprofiler/profiledata.cpp
deleted file mode 100644
index 6082f4aa94..0000000000
--- a/tools/qmlprofiler/profiledata.cpp
+++ /dev/null
@@ -1,1902 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** GNU Lesser General Public License Usage
-** 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, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU General
-** Public License version 3.0 as published by the Free Software Foundation
-** and appearing in the file LICENSE.GPL included in the packaging of this
-** file. Please review the following information to ensure the GNU General
-** Public License version 3.0 requirements will be met:
-** http://www.gnu.org/copyleft/gpl.html.
-**
-** Other Usage
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "profiledata.h"
-#include "constants.h"
-
-#include <QtCore/QStringList>
-#include <QtCore/QRegExp>
-#include <QtCore/QUrl>
-#include <QtCore/QFile>
-#include <QtCore/QXmlStreamReader>
-
-using namespace Constants;
-
-QmlEvent::QmlEvent()
-{
- eventType = QQmlProfilerService::MaximumRangeType;
- eventId = -1;
- duration = 0;
- calls = 0;
- minTime = 0;
- maxTime = 0;
- timePerCall = 0;
- percentOfTime = 0;
- medianTime = 0;
- isBindingLoop = false;
-}
-
-QmlEvent::~QmlEvent()
-{
- qDeleteAll(parentHash.values());
- parentHash.clear();
- qDeleteAll(childrenHash.values());
- childrenHash.clear();
-}
-
-QmlEvent &QmlEvent::operator=(const QmlEvent &ref)
-{
- if (this == &ref)
- return *this;
-
- displayname = ref.displayname;
- location = ref.location;
- eventHashStr = ref.eventHashStr;
- details = ref.details;
- eventType = ref.eventType;
- duration = ref.duration;
- calls = ref.calls;
- minTime = ref.minTime;
- maxTime = ref.maxTime;
- timePerCall = ref.timePerCall;
- percentOfTime = ref.percentOfTime;
- medianTime = ref.medianTime;
- eventId = ref.eventId;
- isBindingLoop = ref.isBindingLoop;
-
- qDeleteAll(parentHash.values());
- parentHash.clear();
- foreach (const QString &key, ref.parentHash.keys()) {
- parentHash.insert(key,
- new QmlEventSub(ref.parentHash.value(key)));
- }
-
- qDeleteAll(childrenHash.values());
- childrenHash.clear();
- foreach (const QString &key, ref.childrenHash.keys()) {
- childrenHash.insert(key,
- new QmlEventSub(ref.childrenHash.value(key)));
- }
-
- return *this;
-}
-
-V8Event::V8Event()
-{
- line = -1;
- eventId = -1;
- totalTime = 0;
- selfTime = 0;
- totalPercent = 0;
- selfPercent = 0;
-}
-
-V8Event::~V8Event()
-{
- qDeleteAll(parentHash.values());
- parentHash.clear();
- qDeleteAll(childrenHash.values());
- childrenHash.clear();
-}
-
-V8Event &V8Event::operator=(const V8Event &ref)
-{
- if (this == &ref)
- return *this;
-
- displayName = ref.displayName;
- filename = ref.filename;
- functionName = ref.functionName;
- line = ref.line;
- totalTime = ref.totalTime;
- totalPercent = ref.totalPercent;
- selfTime = ref.selfTime;
- selfPercent = ref.selfPercent;
- eventId = ref.eventId;
-
- qDeleteAll(parentHash.values());
- parentHash.clear();
- foreach (const QString &key, ref.parentHash.keys()) {
- parentHash.insert(key, new V8EventSub(ref.parentHash.value(key)));
- }
-
- qDeleteAll(childrenHash.values());
- childrenHash.clear();
- foreach (const QString &key, ref.childrenHash.keys()) {
- childrenHash.insert(key, new V8EventSub(ref.childrenHash.value(key)));
- }
- return *this;
-}
-
-// endtimedata
-struct QmlEventEndTime {
- qint64 endTime;
- int startTimeIndex;
- QmlEvent *description;
-};
-
-// starttimedata
-struct QmlEventStartTime{
- qint64 startTime;
- qint64 length;
- qint64 level;
- int endTimeIndex;
- qint64 nestingLevel;
- qint64 nestingDepth;
- QmlEvent *description;
-
- // animation-related data
- int frameRate;
- int animationCount;
-
- int bindingLoopHead;
-};
-
-struct QmlEventTypeCount {
- QList <int> eventIds;
- int nestingCount;
-};
-
-// used by quicksort
-bool compareEndTimes(const QmlEventEndTime &t1,
- const QmlEventEndTime &t2)
-{
- return t1.endTime < t2.endTime;
-}
-
-bool compareStartTimes(const QmlEventStartTime &t1,
- const QmlEventStartTime &t2)
-{
- return t1.startTime < t2.startTime;
-}
-
-bool compareStartIndexes(const QmlEventEndTime &t1,
- const QmlEventEndTime &t2)
-{
- return t1.startTimeIndex < t2.startTimeIndex;
-}
-
-QString qmlEventType(QQmlProfilerService::RangeType typeEnum)
-{
- switch (typeEnum) {
- case QQmlProfilerService::Painting:
- return QLatin1String(TYPE_PAINTING_STR);
- break;
- case QQmlProfilerService::Compiling:
- return QLatin1String(TYPE_COMPILING_STR);
- break;
- case QQmlProfilerService::Creating:
- return QLatin1String(TYPE_CREATING_STR);
- break;
- case QQmlProfilerService::Binding:
- return QLatin1String(TYPE_BINDING_STR);
- break;
- case QQmlProfilerService::HandlingSignal:
- return QLatin1String(TYPE_HANDLINGSIGNAL_STR);
- break;
- default:
- return QString::number((int)typeEnum);
- }
-}
-
-QQmlProfilerService::RangeType qmlEventType(const QString &typeString)
-{
- if (typeString == QLatin1String(TYPE_PAINTING_STR)) {
- return QQmlProfilerService::Painting;
- } else if (typeString == QLatin1String(TYPE_COMPILING_STR)) {
- return QQmlProfilerService::Compiling;
- } else if (typeString == QLatin1String(TYPE_CREATING_STR)) {
- return QQmlProfilerService::Creating;
- } else if (typeString == QLatin1String(TYPE_BINDING_STR)) {
- return QQmlProfilerService::Binding;
- } else if (typeString == QLatin1String(TYPE_HANDLINGSIGNAL_STR)) {
- return QQmlProfilerService::HandlingSignal;
- } else {
- bool isNumber = false;
- int type = typeString.toUInt(&isNumber);
- if (isNumber) {
- return (QQmlProfilerService::RangeType)type;
- } else {
- return QQmlProfilerService::MaximumRangeType;
- }
- }
-}
-
-QString getHashStringForQmlEvent(
- EventLocation location,
- QQmlProfilerService::RangeType eventType)
-{
- return QString("%1:%2:%3:%4").arg(location.filename,
- QString::number(location.line),
- QString::number(location.column),
- QString::number(eventType));
-}
-
-class ProfileDataPrivate
-{
-public:
-
- // convenience functions
- void clearQmlRootEvent();
- void clearV8RootEvent();
-
- // Stored data
- QmlEventHash m_eventDescriptions;
- QList<QmlEventEndTime> m_endTimeSortedList;
- QList<QmlEventStartTime> m_startTimeSortedList;
-
- void collectV8Statistics();
- V8Events m_v8EventList;
- QHash<int, V8Event *> m_v8parents;
-
- QmlEvent m_qmlRootEvent;
- V8Event m_v8RootEvent;
- QString m_rootEventName;
- QString m_rootEventDesc;
-
- QHash<int, QmlEventTypeCount *> m_typeCounts;
-
- qint64 m_traceEndTime;
- qint64 m_traceStartTime;
- qint64 m_qmlMeasuredTime;
- qint64 m_v8MeasuredTime;
-
- QmlEventStartTime *m_lastFrameEvent;
- qint64 m_maximumAnimationCount;
- qint64 m_minimumAnimationCount;
-
- // file to load
- QString m_filename;
-};
-
-ProfileData::ProfileData(QObject *parent) :
- QObject(parent),
- d(new ProfileDataPrivate)
-{
- setObjectName("ProfileData");
-
- d->m_traceEndTime = 0;
- d->m_traceStartTime = -1;
- d->m_qmlMeasuredTime = 0;
- d->m_v8MeasuredTime = 0;
- d->m_rootEventName = tr("<program>");
- d->m_rootEventDesc = tr("Main Program");
- d->clearQmlRootEvent();
- d->clearV8RootEvent();
- d->m_lastFrameEvent = 0;
- d->m_maximumAnimationCount = 0;
- d->m_minimumAnimationCount = 0;
-}
-
-ProfileData::~ProfileData()
-{
- clear();
-}
-
-void ProfileData::clear()
-{
- qDeleteAll(d->m_eventDescriptions.values());
- d->m_eventDescriptions.clear();
-
- qDeleteAll(d->m_v8EventList);
- d->m_v8EventList.clear();
-
- d->m_endTimeSortedList.clear();
- d->m_startTimeSortedList.clear();
-
- d->m_v8parents.clear();
-
- d->clearQmlRootEvent();
- d->clearV8RootEvent();
-
- foreach (QmlEventTypeCount *typeCount, d->m_typeCounts.values())
- delete typeCount;
- d->m_typeCounts.clear();
-
- d->m_traceEndTime = 0;
- d->m_traceStartTime = -1;
- d->m_qmlMeasuredTime = 0;
- d->m_v8MeasuredTime = 0;
-
- d->m_lastFrameEvent = 0;
- d->m_maximumAnimationCount = 0;
- d->m_minimumAnimationCount = 0;
-
- emit countChanged();
- emit dataClear();
-}
-
-QmlEvents ProfileData::getQmlEvents() const
-{
- return d->m_eventDescriptions.values();
-}
-
-QmlEvent *ProfileData::qmlEvent(int eventId) const
-{
- foreach (QmlEvent *event, d->m_eventDescriptions.values()) {
- if (event->eventId == eventId)
- return event;
- }
- return 0;
-}
-
-V8Event *ProfileData::v8Event(int eventId) const
-{
- foreach (V8Event *event, d->m_v8EventList) {
- if (event->eventId == eventId)
- return event;
- }
- return 0;
-}
-
-const V8Events& ProfileData::getV8Events() const
-{
- return d->m_v8EventList;
-}
-
-void ProfileData::addQmlEvent(
- QQmlProfilerService::RangeType type, qint64 startTime, qint64 length,
- const QStringList &data, const EventLocation &location)
-{
- const QChar colon = QLatin1Char(':');
- QString displayName, eventHashStr, details;
- EventLocation eventLocation = location;
-
- emit processingData();
-
- // generate details string
- if (data.isEmpty())
- details = tr("Source code not available");
- else {
- details = data.join(" ").replace('\n'," ").simplified();
- QRegExp rewrite("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)");
- bool match = rewrite.exactMatch(details);
- if (match) {
- details = rewrite.cap(1) + ": " + rewrite.cap(3);
- }
- if (details.startsWith(QString("file://")))
- details = details.mid(details.lastIndexOf(QChar('/')) + 1);
- }
-
- // backwards compatibility: "compiling" events don't have a proper location in older
- // version of the protocol, but the filename is passed in the details string
- if (type == QQmlProfilerService::Compiling && eventLocation.filename.isEmpty()) {
- eventLocation.filename = details;
- eventLocation.line = 1;
- eventLocation.column = 1;
- }
-
- // generate hash
- if (eventLocation.filename.isEmpty()) {
- displayName = tr("<bytecode>");
- eventHashStr = getHashStringForQmlEvent(eventLocation, type);
- } else {
- const QString filePath = QUrl(eventLocation.filename).path();
- displayName = filePath.mid(filePath.lastIndexOf(QChar('/')) + 1) + colon + QString::number(eventLocation.line);
- eventHashStr = getHashStringForQmlEvent(eventLocation, type);
- }
-
- QmlEvent *newEvent;
- if (d->m_eventDescriptions.contains(eventHashStr)) {
- newEvent = d->m_eventDescriptions[eventHashStr];
- } else {
- newEvent = new QmlEvent;
- newEvent->displayname = displayName;
- newEvent->location = eventLocation;
- newEvent->eventHashStr = eventHashStr;
- newEvent->eventType = type;
- newEvent->details = details;
- d->m_eventDescriptions.insert(eventHashStr, newEvent);
- }
-
- QmlEventEndTime endTimeData;
- endTimeData.endTime = startTime + length;
- endTimeData.description = newEvent;
- endTimeData.startTimeIndex = d->m_startTimeSortedList.count();
-
- QmlEventStartTime startTimeData;
- startTimeData.startTime = startTime;
- startTimeData.length = length;
- startTimeData.description = newEvent;
- startTimeData.endTimeIndex = d->m_endTimeSortedList.count();
- startTimeData.animationCount = -1;
- startTimeData.frameRate = 1e9/length;
-
- d->m_endTimeSortedList << endTimeData;
- d->m_startTimeSortedList << startTimeData;
-
- emit countChanged();
-}
-
-void ProfileData::addV8Event(int depth, const QString &function,
- const QString &filename, int lineNumber,
- double totalTime, double selfTime)
-{
- QString displayName = filename.mid(
- filename.lastIndexOf(QLatin1Char('/')) + 1) + QLatin1Char(':') +
- QString::number(lineNumber);
- V8Event *eventData = 0;
-
- // time is given in milliseconds, but internally we store it in microseconds
- totalTime *= 1e6;
- selfTime *= 1e6;
-
- // cumulate information
- foreach (V8Event *v8event, d->m_v8EventList) {
- if (v8event->displayName == displayName &&
- v8event->functionName == function) {
- eventData = v8event;
- break;
- }
- }
-
- if (!eventData) {
- eventData = new V8Event;
- eventData->displayName = displayName;
- eventData->filename = filename;
- eventData->functionName = function;
- eventData->line = lineNumber;
- eventData->totalTime = totalTime;
- eventData->selfTime = selfTime;
- d->m_v8EventList << eventData;
- } else {
- eventData->totalTime += totalTime;
- eventData->selfTime += selfTime;
- }
- d->m_v8parents[depth] = eventData;
-
- V8Event *parentEvent = 0;
- if (depth == 0) {
- parentEvent = &d->m_v8RootEvent;
- d->m_v8MeasuredTime += totalTime;
- }
- if (depth > 0 && d->m_v8parents.contains(depth-1)) {
- parentEvent = d->m_v8parents.value(depth-1);
- }
-
- if (parentEvent != 0) {
- if (!eventData->parentHash.contains(parentEvent->displayName)) {
- V8EventSub *newParentSub = new V8EventSub(parentEvent);
- newParentSub->totalTime = totalTime;
-
- eventData->parentHash.insert(parentEvent->displayName, newParentSub );
- } else {
- V8EventSub *newParentSub =
- eventData->parentHash.value(parentEvent->displayName);
- newParentSub->totalTime += totalTime;
- }
-
- if (!parentEvent->childrenHash.contains(eventData->displayName)) {
- V8EventSub *newChildSub = new V8EventSub(eventData);
- newChildSub->totalTime = totalTime;
-
- parentEvent->childrenHash.insert(eventData->displayName, newChildSub);
- } else {
- V8EventSub *newChildSub =
- parentEvent->childrenHash.value(eventData->displayName);
- newChildSub->totalTime += totalTime;
- }
- }
-}
-
-void ProfileData::addFrameEvent(qint64 time, int framerate, int animationcount)
-{
- QString displayName, eventHashStr, details;
-
- emit processingData();
-
- details = tr("Animation Timer Update");
- displayName = tr("<Animation Update>");
- eventHashStr = displayName;
-
- QmlEvent *newEvent;
- if (d->m_eventDescriptions.contains(eventHashStr)) {
- newEvent = d->m_eventDescriptions[eventHashStr];
- } else {
- newEvent = new QmlEvent;
- newEvent->displayname = displayName;
- newEvent->eventHashStr = eventHashStr;
- newEvent->eventType = QQmlProfilerService::Painting;
- newEvent->details = details;
- d->m_eventDescriptions.insert(eventHashStr, newEvent);
- }
-
- qint64 length = 1e9/framerate;
- // avoid overlap
- if (d->m_lastFrameEvent &&
- d->m_lastFrameEvent->startTime + d->m_lastFrameEvent->length >= time) {
- d->m_lastFrameEvent->length = time - 1 - d->m_lastFrameEvent->startTime;
- d->m_endTimeSortedList[d->m_lastFrameEvent->endTimeIndex].endTime =
- d->m_lastFrameEvent->startTime + d->m_lastFrameEvent->length;
- }
-
- QmlEventEndTime endTimeData;
- endTimeData.endTime = time + length;
- endTimeData.description = newEvent;
- endTimeData.startTimeIndex = d->m_startTimeSortedList.count();
-
- QmlEventStartTime startTimeData;
- startTimeData.startTime = time;
- startTimeData.length = length;
- startTimeData.description = newEvent;
- startTimeData.endTimeIndex = d->m_endTimeSortedList.count();
- startTimeData.animationCount = animationcount;
- startTimeData.frameRate = framerate;
-
- d->m_endTimeSortedList << endTimeData;
- d->m_startTimeSortedList << startTimeData;
-
- d->m_lastFrameEvent = &d->m_startTimeSortedList.last();
-
- emit countChanged();
-}
-
-void ProfileDataPrivate::collectV8Statistics()
-{
- if (!m_v8EventList.isEmpty()) {
- double totalTimes = m_v8MeasuredTime;
- double selfTimes = 0;
- foreach (V8Event *v8event, m_v8EventList) {
- selfTimes += v8event->selfTime;
- }
-
- // prevent divisions by 0
- if (totalTimes == 0)
- totalTimes = 1;
- if (selfTimes == 0)
- selfTimes = 1;
-
- // insert root event in eventlist
- // the +1 ns is to get it on top of the sorted list
- m_v8RootEvent.totalTime = m_v8MeasuredTime + 1;
- m_v8RootEvent.selfTime = 0;
-
- int rootEventIndex = -1;
- for (int ndx = 0; ndx < m_v8EventList.count(); ndx++)
- {
- if (m_v8EventList.at(ndx)->displayName == m_rootEventName) {
- m_v8RootEvent = *m_v8EventList.at(ndx);
- rootEventIndex = ndx;
- break;
- }
- }
- if (rootEventIndex == -1) {
- rootEventIndex = m_v8EventList.count();
- V8Event *newRootEvent = new V8Event;
- *newRootEvent = m_v8RootEvent;
- m_v8EventList << newRootEvent;
- }
-
- foreach (V8Event *v8event, m_v8EventList) {
- v8event->totalPercent = v8event->totalTime * 100.0 / totalTimes;
- v8event->selfPercent = v8event->selfTime * 100.0 / selfTimes;
- }
-
- int index = 0;
- foreach (V8Event *v8event, m_v8EventList) {
- v8event->eventId = index++;
- }
- m_v8RootEvent.eventId = m_v8EventList[rootEventIndex]->eventId;
- }
-}
-
-void ProfileData::setTraceEndTime( qint64 time )
-{
- d->m_traceEndTime = time;
-}
-
-void ProfileData::setTraceStartTime( qint64 time )
-{
- d->m_traceStartTime = time;
-}
-
-void ProfileData::complete()
-{
- emit postProcessing();
- d->collectV8Statistics();
- postProcess();
-}
-
-void ProfileDataPrivate::clearQmlRootEvent()
-{
- m_qmlRootEvent.displayname = m_rootEventName;
- m_qmlRootEvent.location = EventLocation();
- m_qmlRootEvent.eventHashStr = m_rootEventName;
- m_qmlRootEvent.details = m_rootEventDesc;
- m_qmlRootEvent.eventType = QQmlProfilerService::Binding;
- m_qmlRootEvent.duration = 0;
- m_qmlRootEvent.calls = 0;
- m_qmlRootEvent.minTime = 0;
- m_qmlRootEvent.maxTime = 0;
- m_qmlRootEvent.timePerCall = 0;
- m_qmlRootEvent.percentOfTime = 0;
- m_qmlRootEvent.medianTime = 0;
- m_qmlRootEvent.eventId = -1;
-
- qDeleteAll(m_qmlRootEvent.parentHash.values());
- qDeleteAll(m_qmlRootEvent.childrenHash.values());
- m_qmlRootEvent.parentHash.clear();
- m_qmlRootEvent.childrenHash.clear();
-}
-
-void ProfileDataPrivate::clearV8RootEvent()
-{
- m_v8RootEvent.displayName = m_rootEventName;
- m_v8RootEvent.functionName = m_rootEventDesc;
- m_v8RootEvent.line = -1;
- m_v8RootEvent.totalTime = 0;
- m_v8RootEvent.totalPercent = 0;
- m_v8RootEvent.selfTime = 0;
- m_v8RootEvent.selfPercent = 0;
- m_v8RootEvent.eventId = -1;
-
- qDeleteAll(m_v8RootEvent.parentHash.values());
- qDeleteAll(m_v8RootEvent.childrenHash.values());
- m_v8RootEvent.parentHash.clear();
- m_v8RootEvent.childrenHash.clear();
-}
-
-void ProfileData::compileStatistics(qint64 startTime, qint64 endTime)
-{
- int index;
- int fromIndex = findFirstIndex(startTime);
- int toIndex = findLastIndex(endTime);
- double totalTime = 0;
-
- // clear existing statistics
- foreach (QmlEvent *eventDescription,
- d->m_eventDescriptions.values()) {
- eventDescription->calls = 0;
- // maximum possible value
- eventDescription->minTime = d->m_endTimeSortedList.last().endTime;
- eventDescription->maxTime = 0;
- eventDescription->medianTime = 0;
- eventDescription->duration = 0;
- qDeleteAll(eventDescription->parentHash);
- qDeleteAll(eventDescription->childrenHash);
- eventDescription->parentHash.clear();
- eventDescription->childrenHash.clear();
- }
-
- // create root event for statistics
- d->clearQmlRootEvent();
-
- // compute parent-child relationship and call count
- QHash<int, QmlEvent*> lastParent;
- for (index = fromIndex; index <= toIndex; index++) {
- QmlEvent *eventDescription =
- d->m_startTimeSortedList[index].description;
-
- if (d->m_startTimeSortedList[index].startTime > endTime ||
- d->m_startTimeSortedList[index].startTime +
- d->m_startTimeSortedList[index].length < startTime) {
- continue;
- }
-
- if (eventDescription->eventType == QQmlProfilerService::Painting) {
- // skip animation/paint events
- continue;
- }
-
- eventDescription->calls++;
- qint64 duration = d->m_startTimeSortedList[index].length;
- eventDescription->duration += duration;
- if (eventDescription->maxTime < duration)
- eventDescription->maxTime = duration;
- if (eventDescription->minTime > duration)
- eventDescription->minTime = duration;
-
- int level = d->m_startTimeSortedList[index].level;
-
- QmlEvent *parentEvent = &d->m_qmlRootEvent;
- if (level > MIN_LEVEL && lastParent.contains(level-1)) {
- parentEvent = lastParent[level-1];
- }
-
- if (!eventDescription->parentHash.contains(parentEvent->eventHashStr)) {
- QmlEventSub *newParentEvent =
- new QmlEventSub(parentEvent);
- newParentEvent->calls = 1;
- newParentEvent->duration = duration;
-
- eventDescription->parentHash.insert(parentEvent->eventHashStr,
- newParentEvent);
- } else {
- QmlEventSub *newParentEvent =
- eventDescription->parentHash.value(parentEvent->eventHashStr);
- newParentEvent->duration += duration;
- newParentEvent->calls++;
- }
-
- if (!parentEvent->childrenHash.contains(eventDescription->eventHashStr)) {
- QmlEventSub *newChildEvent =
- new QmlEventSub(eventDescription);
- newChildEvent->calls = 1;
- newChildEvent->duration = duration;
-
- parentEvent->childrenHash.insert(eventDescription->eventHashStr,
- newChildEvent);
- } else {
- QmlEventSub *newChildEvent =
- parentEvent->childrenHash.value(eventDescription->eventHashStr);
- newChildEvent->duration += duration;
- newChildEvent->calls++;
- }
-
- lastParent[level] = eventDescription;
-
- if (level == MIN_LEVEL) {
- totalTime += duration;
- }
- }
-
- // fake rootEvent statistics
- // the +1 nanosecond is to force it to be on top of the sorted list
- d->m_qmlRootEvent.duration = totalTime+1;
- d->m_qmlRootEvent.minTime = totalTime+1;
- d->m_qmlRootEvent.maxTime = totalTime+1;
- d->m_qmlRootEvent.medianTime = totalTime+1;
- if (totalTime > 0)
- d->m_qmlRootEvent.calls = 1;
-
- // insert into list
- QmlEvent *listedRootEvent =
- d->m_eventDescriptions.value(d->m_rootEventName);
- if (!listedRootEvent) {
- listedRootEvent = new QmlEvent;
- d->m_eventDescriptions.insert(d->m_rootEventName, listedRootEvent);
- }
- *listedRootEvent = d->m_qmlRootEvent;
-
- // compute percentages
- foreach (QmlEvent *binding, d->m_eventDescriptions.values()) {
- binding->percentOfTime = binding->duration * 100.0 / totalTime;
- binding->timePerCall = binding->calls > 0 ?
- double(binding->duration) / binding->calls : 0;
- }
-
- // compute median time
- QHash < QmlEvent* , QList<qint64> > durationLists;
- for (index = fromIndex; index <= toIndex; index++) {
- QmlEvent *desc = d->m_startTimeSortedList[index].description;
- qint64 len = d->m_startTimeSortedList[index].length;
- durationLists[desc].append(len);
- }
- QMutableHashIterator < QmlEvent* , QList<qint64> > iter(durationLists);
- while (iter.hasNext()) {
- iter.next();
- if (!iter.value().isEmpty()) {
- qSort(iter.value());
- iter.key()->medianTime = iter.value().at(iter.value().count()/2);
- }
- }
-}
-
-void ProfileData::prepareForDisplay()
-{
- // generate numeric ids
- int ndx = 0;
- foreach (QmlEvent *binding, d->m_eventDescriptions.values()) {
- binding->eventId = ndx++;
- }
-
- // collect type counts
- foreach (const QmlEventStartTime &eventStartData,
- d->m_startTimeSortedList) {
- int typeNumber = eventStartData.description->eventType;
- if (!d->m_typeCounts.contains(typeNumber)) {
- d->m_typeCounts[typeNumber] = new QmlEventTypeCount;
- d->m_typeCounts[typeNumber]->nestingCount = 0;
- }
- if (eventStartData.nestingLevel >
- d->m_typeCounts[typeNumber]->nestingCount) {
- d->m_typeCounts[typeNumber]->nestingCount = eventStartData.nestingLevel;
- }
- if (!d->m_typeCounts[typeNumber]->eventIds.contains(
- eventStartData.description->eventId))
- d->m_typeCounts[typeNumber]->eventIds << eventStartData.description->eventId;
- }
-}
-
-void ProfileData::sortStartTimes()
-{
- if (d->m_startTimeSortedList.count() < 2)
- return;
-
- // assuming startTimes is partially sorted
- // identify blocks of events and sort them with quicksort
- QList<QmlEventStartTime>::iterator itFrom =
- d->m_startTimeSortedList.end() - 2;
- QList<QmlEventStartTime>::iterator itTo =
- d->m_startTimeSortedList.end() - 1;
-
- while (itFrom != d->m_startTimeSortedList.begin() &&
- itTo != d->m_startTimeSortedList.begin()) {
- // find block to sort
- while ( itFrom != d->m_startTimeSortedList.begin()
- && itTo->startTime > itFrom->startTime ) {
- itTo--;
- itFrom = itTo - 1;
- }
-
- // if we're at the end of the list
- if (itFrom == d->m_startTimeSortedList.begin())
- break;
-
- // find block length
- while ( itFrom != d->m_startTimeSortedList.begin()
- && itTo->startTime <= itFrom->startTime )
- itFrom--;
-
- if (itTo->startTime <= itFrom->startTime)
- qSort(itFrom, itTo + 1, compareStartTimes);
- else
- qSort(itFrom + 1, itTo + 1, compareStartTimes);
-
- // move to next block
- itTo = itFrom;
- itFrom = itTo - 1;
- }
-
- // link back the endTimes
- for (int i = 0; i < d->m_startTimeSortedList.length(); i++)
- d->m_endTimeSortedList[d->m_startTimeSortedList[i].endTimeIndex].startTimeIndex = i;
-}
-
-void ProfileData::sortEndTimes()
-{
- // assuming endTimes is partially sorted
- // identify blocks of events and sort them with quicksort
-
- if (d->m_endTimeSortedList.count() < 2)
- return;
-
- QList<QmlEventEndTime>::iterator itFrom =
- d->m_endTimeSortedList.begin();
- QList<QmlEventEndTime>::iterator itTo =
- d->m_endTimeSortedList.begin() + 1;
-
- while (itTo != d->m_endTimeSortedList.end() &&
- itFrom != d->m_endTimeSortedList.end()) {
- // find block to sort
- while ( itTo != d->m_endTimeSortedList.end()
- && d->m_startTimeSortedList[itTo->startTimeIndex].startTime >
- d->m_startTimeSortedList[itFrom->startTimeIndex].startTime +
- d->m_startTimeSortedList[itFrom->startTimeIndex].length ) {
- itFrom++;
- itTo = itFrom+1;
- }
-
- // if we're at the end of the list
- if (itTo == d->m_endTimeSortedList.end())
- break;
-
- // find block length
- while ( itTo != d->m_endTimeSortedList.end()
- && d->m_startTimeSortedList[itTo->startTimeIndex].startTime <=
- d->m_startTimeSortedList[itFrom->startTimeIndex].startTime +
- d->m_startTimeSortedList[itFrom->startTimeIndex].length )
- itTo++;
-
- // sort block
- qSort(itFrom, itTo, compareEndTimes);
-
- // move to next block
- itFrom = itTo;
- itTo = itFrom+1;
-
- }
-
- // link back the startTimes
- for (int i = 0; i < d->m_endTimeSortedList.length(); i++)
- d->m_startTimeSortedList[d->m_endTimeSortedList[i].startTimeIndex].endTimeIndex = i;
-}
-
-void ProfileData::findAnimationLimits()
-{
- d->m_maximumAnimationCount = 0;
- d->m_minimumAnimationCount = 0;
- d->m_lastFrameEvent = 0;
-
- for (int i = 0; i < d->m_startTimeSortedList.count(); i++) {
- if (d->m_startTimeSortedList[i].description->eventType ==
- QQmlProfilerService::Painting &&
- d->m_startTimeSortedList[i].animationCount >= 0) {
- int animationcount = d->m_startTimeSortedList[i].animationCount;
- if (d->m_lastFrameEvent) {
- if (animationcount > d->m_maximumAnimationCount)
- d->m_maximumAnimationCount = animationcount;
- if (animationcount < d->m_minimumAnimationCount)
- d->m_minimumAnimationCount = animationcount;
- } else {
- d->m_maximumAnimationCount = animationcount;
- d->m_minimumAnimationCount = animationcount;
- }
- d->m_lastFrameEvent = &d->m_startTimeSortedList[i];
- }
- }
-}
-
-void ProfileData::computeNestingLevels()
-{
- // compute levels
- QHash <int, qint64> endtimesPerLevel;
- QList <int> nestingLevels;
- QList < QHash <int, qint64> > endtimesPerNestingLevel;
- int level = MIN_LEVEL;
- endtimesPerLevel[MIN_LEVEL] = 0;
-
- for (int i = 0; i < QQmlProfilerService::MaximumRangeType; i++) {
- nestingLevels << MIN_LEVEL;
- QHash <int, qint64> dummyHash;
- dummyHash[MIN_LEVEL] = 0;
- endtimesPerNestingLevel << dummyHash;
- }
-
- for (int i=0; i<d->m_startTimeSortedList.count(); i++) {
- qint64 st = d->m_startTimeSortedList[i].startTime;
- int type = d->m_startTimeSortedList[i].description->eventType;
-
- if (type == QQmlProfilerService::Painting) {
- // animation/paint events have level 1 by definition,
- // but are not considered parents of other events for
- // statistical purposes
- d->m_startTimeSortedList[i].level = MIN_LEVEL;
- d->m_startTimeSortedList[i].nestingLevel = MIN_LEVEL;
- continue;
- }
-
- // general level
- if (endtimesPerLevel[level] > st) {
- level++;
- } else {
- while (level > MIN_LEVEL && endtimesPerLevel[level-1] <= st)
- level--;
- }
- endtimesPerLevel[level] = st + d->m_startTimeSortedList[i].length;
-
- // per type
- if (endtimesPerNestingLevel[type][nestingLevels[type]] > st) {
- nestingLevels[type]++;
- } else {
- while (nestingLevels[type] > MIN_LEVEL &&
- endtimesPerNestingLevel[type][nestingLevels[type]-1] <= st)
- nestingLevels[type]--;
- }
- endtimesPerNestingLevel[type][nestingLevels[type]] = st +
- d->m_startTimeSortedList[i].length;
-
- d->m_startTimeSortedList[i].level = level;
- d->m_startTimeSortedList[i].nestingLevel = nestingLevels[type];
-
- if (level == MIN_LEVEL) {
- d->m_qmlMeasuredTime += d->m_startTimeSortedList[i].length;
- }
- }
-}
-
-void ProfileData::computeNestingDepth()
-{
- QHash <int, int> nestingDepth;
- for (int i = 0; i < d->m_endTimeSortedList.count(); i++) {
- int type = d->m_endTimeSortedList[i].description->eventType;
- int nestingInType = d->m_startTimeSortedList[d->m_endTimeSortedList[i].startTimeIndex].nestingLevel;
- if (!nestingDepth.contains(type))
- nestingDepth[type] = nestingInType;
- else {
- int nd = nestingDepth[type];
- nestingDepth[type] = nd > nestingInType ? nd : nestingInType;
- }
-
- d->m_startTimeSortedList[d->m_endTimeSortedList[i].startTimeIndex].nestingDepth
- = nestingDepth[type];
- if (nestingInType == MIN_LEVEL)
- nestingDepth[type] = MIN_LEVEL;
- }
-}
-
-void ProfileData::postProcess()
-{
- if (count() != 0) {
- sortStartTimes();
- sortEndTimes();
- findAnimationLimits();
- computeLevels();
- linkEndsToStarts();
- reloadDetails();
- compileStatistics(traceStartTime(), traceEndTime());
- prepareForDisplay();
- }
- // data is ready even when there's no data
- emit dataReady();
-}
-
-void ProfileData::linkEndsToStarts()
-{
- for (int i = 0; i < d->m_startTimeSortedList.count(); i++)
- d->m_endTimeSortedList[d->m_startTimeSortedList[i].endTimeIndex].startTimeIndex = i;
-}
-
-void ProfileData::computeLevels()
-{
- computeNestingLevels();
- computeNestingDepth();
-}
-
-void ProfileData::reloadDetails()
-{
- // request binding/signal details from the AST
- foreach (QmlEvent *event, d->m_eventDescriptions.values()) {
- if (event->eventType != QQmlProfilerService::Binding &&
- event->eventType != QQmlProfilerService::HandlingSignal)
- continue;
-
- // This skips anonymous bindings in Qt4.8 (we don't have valid location data for them)
- if (event->location.filename.isEmpty())
- continue;
-
- // Skip non-anonymous bindings from Qt4.8 (we already have correct details for them)
- if (event->location.column == -1)
- continue;
-
- emit requestDetailsForLocation(event->eventType, event->location);
- }
- emit reloadDocumentsForDetails();
-}
-
-void ProfileData::findBindingLoops(qint64 startTime, qint64 endTime)
-{
- // first clear existing data
- foreach (QmlEvent *event, d->m_eventDescriptions.values()) {
- event->isBindingLoop = false;
- foreach (QmlEventSub *parentEvent, event->parentHash.values())
- parentEvent->inLoopPath = false;
- foreach (QmlEventSub *childEvent, event->childrenHash.values())
- childEvent->inLoopPath = false;
- }
-
- QList <QmlEvent *> stackRefs;
- QList <QmlEventStartTime *> stack;
- int fromIndex = findFirstIndex(startTime);
- int toIndex = findLastIndex(endTime);
-
- for (int i = 0; i < d->m_startTimeSortedList.count(); i++) {
- QmlEvent *currentEvent = d->m_startTimeSortedList[i].description;
- QmlEventStartTime *inTimeEvent = &d->m_startTimeSortedList[i];
- inTimeEvent->bindingLoopHead = -1;
-
- // managing call stack
- for (int j = stack.count() - 1; j >= 0; j--) {
- if (stack[j]->startTime + stack[j]->length <= inTimeEvent->startTime) {
- stack.removeAt(j);
- stackRefs.removeAt(j);
- }
- }
-
- bool loopDetected = stackRefs.contains(currentEvent);
- stack << inTimeEvent;
- stackRefs << currentEvent;
-
- if (loopDetected) {
- if (i >= fromIndex && i <= toIndex) {
- // for the statistics
- currentEvent->isBindingLoop = true;
- for (int j = stackRefs.indexOf(currentEvent); j < stackRefs.count()-1; j++) {
- QmlEventSub *nextEventSub = stackRefs[j]->childrenHash.value(stackRefs[j+1]->eventHashStr);
- nextEventSub->inLoopPath = true;
- QmlEventSub *prevEventSub = stackRefs[j+1]->parentHash.value(stackRefs[j]->eventHashStr);
- prevEventSub->inLoopPath = true;
- }
- }
-
- // use crossed references to find index in starttimesortedlist
- QmlEventStartTime *head = stack[stackRefs.indexOf(currentEvent)];
- inTimeEvent->bindingLoopHead = d->m_endTimeSortedList[head->endTimeIndex].startTimeIndex;
- d->m_startTimeSortedList[inTimeEvent->bindingLoopHead].bindingLoopHead = i;
- }
- }
-}
-
-void ProfileData::rewriteDetailsString(
- QQmlProfilerService::RangeType eventType,
- const EventLocation &location, const QString &newString)
-{
- QString eventHashStr = getHashStringForQmlEvent(location,
- eventType);
- Q_ASSERT(d->m_eventDescriptions.contains(eventHashStr));
- d->m_eventDescriptions.value(eventHashStr)->details = newString;
- emit detailsChanged(d->m_eventDescriptions.value(eventHashStr)->eventId,
- newString);
-}
-
-void ProfileData::finishedRewritingDetails()
-{
- emit reloadDetailLabels();
-}
-
-// get list of events between A and B:
-// find fist event with endtime after A -> aa
-// find last event with starttime before B -> bb
-// list is from parent of aa with level=0 to bb, in the "sorted by starttime" list
-int ProfileData::findFirstIndex(qint64 startTime) const
-{
- int candidate = -1;
- // in the "endtime" list, find the first event that ends after startTime
- if (d->m_endTimeSortedList.isEmpty())
- return 0; // -1
- if (d->m_endTimeSortedList.length() == 1 ||
- d->m_endTimeSortedList.first().endTime >= startTime)
- candidate = 0;
- else
- if (d->m_endTimeSortedList.last().endTime <= startTime)
- return 0; // -1
-
- if (candidate == -1)
- {
- int fromIndex = 0;
- int toIndex = d->m_endTimeSortedList.count()-1;
- while (toIndex - fromIndex > 1) {
- int midIndex = (fromIndex + toIndex)/2;
- if (d->m_endTimeSortedList[midIndex].endTime < startTime)
- fromIndex = midIndex;
- else
- toIndex = midIndex;
- }
-
- candidate = toIndex;
- }
-
- int ndx = d->m_endTimeSortedList[candidate].startTimeIndex;
-
- // and then go to the parent
- while (d->m_startTimeSortedList[ndx].level != MIN_LEVEL && ndx > 0)
- ndx--;
-
- return ndx;
-}
-
-int ProfileData::findFirstIndexNoParents(qint64 startTime) const
-{
- int candidate = -1;
- // in the "endtime" list, find the first event that ends after startTime
- if (d->m_endTimeSortedList.isEmpty())
- return 0; // -1
- if (d->m_endTimeSortedList.length() == 1 ||
- d->m_endTimeSortedList.first().endTime >= startTime)
- candidate = 0;
- else
- if (d->m_endTimeSortedList.last().endTime <= startTime)
- return 0; // -1
-
- if (candidate == -1) {
- int fromIndex = 0;
- int toIndex = d->m_endTimeSortedList.count()-1;
- while (toIndex - fromIndex > 1) {
- int midIndex = (fromIndex + toIndex)/2;
- if (d->m_endTimeSortedList[midIndex].endTime < startTime)
- fromIndex = midIndex;
- else
- toIndex = midIndex;
- }
-
- candidate = toIndex;
- }
-
- int ndx = d->m_endTimeSortedList[candidate].startTimeIndex;
-
- return ndx;
-}
-
-int ProfileData::findLastIndex(qint64 endTime) const
-{
- // in the "starttime" list, find the last event that starts before endtime
- if (d->m_startTimeSortedList.isEmpty())
- return 0; // -1
- if (d->m_startTimeSortedList.first().startTime >= endTime)
- return 0; // -1
- if (d->m_startTimeSortedList.length() == 1)
- return 0;
- if (d->m_startTimeSortedList.last().startTime <= endTime)
- return d->m_startTimeSortedList.count()-1;
-
- int fromIndex = 0;
- int toIndex = d->m_startTimeSortedList.count()-1;
- while (toIndex - fromIndex > 1) {
- int midIndex = (fromIndex + toIndex)/2;
- if (d->m_startTimeSortedList[midIndex].startTime < endTime)
- fromIndex = midIndex;
- else
- toIndex = midIndex;
- }
-
- return fromIndex;
-}
-
-qint64 ProfileData::firstTimeMark() const
-{
- if (d->m_startTimeSortedList.isEmpty())
- return 0;
- else {
- return d->m_startTimeSortedList[0].startTime;
- }
-}
-
-qint64 ProfileData::lastTimeMark() const
-{
- if (d->m_endTimeSortedList.isEmpty())
- return 0;
- else {
- return d->m_endTimeSortedList.last().endTime;
- }
-}
-
-qint64 ProfileData::traceStartTime() const
-{
- return d->m_traceStartTime != -1? d->m_traceStartTime : firstTimeMark();
-}
-
-qint64 ProfileData::traceEndTime() const
-{
- return d->m_traceEndTime ? d->m_traceEndTime : lastTimeMark();
-}
-
-qint64 ProfileData::traceDuration() const
-{
- return traceEndTime() - traceStartTime();
-}
-
-qint64 ProfileData::qmlMeasuredTime() const
-{
- return d->m_qmlMeasuredTime;
-}
-qint64 ProfileData::v8MeasuredTime() const
-{
- return d->m_v8MeasuredTime;
-}
-
-int ProfileData::count() const
-{
- return d->m_startTimeSortedList.count();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-
-bool ProfileData::save(const QString &filename)
-{
- if (count() == 0) {
- emit error(tr("No data to save"));
- return false;
- }
-
- QFile file(filename);
- if (!file.open(QIODevice::WriteOnly)) {
- emit error(tr("Could not open %1 for writing").arg(filename));
- return false;
- }
-
- QXmlStreamWriter stream(&file);
- stream.setAutoFormatting(true);
- stream.writeStartDocument();
-
- stream.writeStartElement("trace");
- stream.writeAttribute("version", PROFILER_FILE_VERSION);
-
- stream.writeAttribute("traceStart", QString::number(traceStartTime()));
- stream.writeAttribute("traceEnd", QString::number(traceEndTime()));
-
- stream.writeStartElement("eventData");
- stream.writeAttribute("totalTime", QString::number(d->m_qmlMeasuredTime));
-
- foreach (const QmlEvent *eventData, d->m_eventDescriptions.values()) {
- stream.writeStartElement("event");
- stream.writeAttribute("index",
- QString::number(
- d->m_eventDescriptions.keys().indexOf(
- eventData->eventHashStr)));
- stream.writeTextElement("displayname", eventData->displayname);
- stream.writeTextElement("type", qmlEventType(eventData->eventType));
- if (!eventData->location.filename.isEmpty()) {
- stream.writeTextElement("filename", eventData->location.filename);
- stream.writeTextElement("line",
- QString::number(eventData->location.line));
- stream.writeTextElement("column",
- QString::number(eventData->location.column));
- }
- stream.writeTextElement("details", eventData->details);
- stream.writeEndElement();
- }
- stream.writeEndElement(); // eventData
-
- stream.writeStartElement("eventList");
- foreach (const QmlEventStartTime &rangedEvent,
- d->m_startTimeSortedList) {
- stream.writeStartElement("range");
- stream.writeAttribute("startTime", QString::number(rangedEvent.startTime));
- stream.writeAttribute("duration", QString::number(rangedEvent.length));
- stream.writeAttribute("eventIndex",
- QString::number(d->m_eventDescriptions.keys().indexOf(
- rangedEvent.description->eventHashStr)));
- if (rangedEvent.description->eventType ==
- QQmlProfilerService::Painting && rangedEvent.animationCount >= 0) {
- // animation frame
- stream.writeAttribute("framerate",
- QString::number(rangedEvent.frameRate));
- stream.writeAttribute("animationcount",
- QString::number(rangedEvent.animationCount));
- }
- stream.writeEndElement();
- }
- stream.writeEndElement(); // eventList
-
- stream.writeStartElement("v8profile"); // v8 profiler output
- stream.writeAttribute("totalTime", QString::number(d->m_v8MeasuredTime));
- foreach (V8Event *v8event, d->m_v8EventList) {
- stream.writeStartElement("event");
- stream.writeAttribute("index",
- QString::number(d->m_v8EventList.indexOf(v8event)));
- stream.writeTextElement("displayname", v8event->displayName);
- stream.writeTextElement("functionname", v8event->functionName);
- if (!v8event->filename.isEmpty()) {
- stream.writeTextElement("filename", v8event->filename);
- stream.writeTextElement("line", QString::number(v8event->line));
- }
- stream.writeTextElement("totalTime",
- QString::number(v8event->totalTime));
- stream.writeTextElement("selfTime", QString::number(v8event->selfTime));
- if (!v8event->childrenHash.isEmpty()) {
- stream.writeStartElement("childrenEvents");
- QStringList childrenIndexes;
- QStringList childrenTimes;
- QStringList parentTimes;
- foreach (V8EventSub *v8child, v8event->childrenHash.values()) {
- childrenIndexes << QString::number(v8child->reference->eventId);
- childrenTimes << QString::number(v8child->totalTime);
- parentTimes << QString::number(
- d->m_v8EventList[v8child->reference->eventId]->
- parentHash[v8event->displayName]->totalTime);
- }
-
- stream.writeAttribute("list", childrenIndexes.join(QString(", ")));
- stream.writeAttribute("childrenTimes",
- childrenTimes.join(QString(", ")));
- stream.writeAttribute("parentTimes",
- parentTimes.join(QString(", ")));
- stream.writeEndElement();
- }
- stream.writeEndElement();
- }
- stream.writeEndElement(); // v8 profiler output
-
- stream.writeEndElement(); // trace
- stream.writeEndDocument();
-
- file.close();
- return true;
-}
-
-void ProfileData::setFilename(const QString &filename)
-{
- d->m_filename = filename;
-}
-
-void ProfileData::load(const QString &filename)
-{
- setFilename(filename);
- load();
-}
-
-// "be strict in your output but tolerant in your inputs"
-void ProfileData::load()
-{
- QString filename = d->m_filename;
-
- QFile file(filename);
-
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- emit error(tr("Could not open %1 for reading").arg(filename));
- return;
- }
-
- emit processingData();
-
- // erase current
- clear();
-
- bool readingQmlEvents = false;
- bool readingV8Events = false;
- QHash <int, QmlEvent *> descriptionBuffer;
- QmlEvent *currentEvent = 0;
- QHash <int, V8Event *> v8eventBuffer;
- QHash <int, QString> childrenIndexes;
- QHash <int, QString> childrenTimes;
- QHash <int, QString> parentTimes;
- V8Event *v8event = 0;
- bool startTimesAreSorted = true;
- bool validVersion = true;
-
- // time computation
- d->m_v8MeasuredTime = 0;
- d->m_qmlMeasuredTime = 0;
- double cumulatedV8Time = 0;
-
- QXmlStreamReader stream(&file);
-
- while (validVersion && !stream.atEnd() && !stream.hasError()) {
- QXmlStreamReader::TokenType token = stream.readNext();
- QString elementName = stream.name().toString();
- switch (token) {
- case QXmlStreamReader::StartDocument : continue;
- case QXmlStreamReader::StartElement : {
- if (elementName == "trace") {
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("version"))
- validVersion =
- attributes.value("version").toString() ==
- PROFILER_FILE_VERSION;
- else
- validVersion = false;
- if (attributes.hasAttribute("traceStart"))
- setTraceStartTime(attributes.value("traceStart").
- toString().toLongLong());
- if (attributes.hasAttribute("traceEnd"))
- setTraceEndTime(attributes.value("traceEnd").
- toString().toLongLong());
- }
- if (elementName == "eventData" && !readingV8Events) {
- readingQmlEvents = true;
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("totalTime"))
- d->m_qmlMeasuredTime = attributes.value("totalTime").
- toString().toDouble();
- break;
- }
- if (elementName == "v8profile" && !readingQmlEvents) {
- readingV8Events = true;
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("totalTime"))
- d->m_v8MeasuredTime = attributes.value("totalTime").
- toString().toDouble();
- break;
- }
-
- if (elementName == "trace") {
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("traceStart"))
- setTraceStartTime(attributes.value("traceStart").
- toString().toLongLong());
- if (attributes.hasAttribute("traceEnd"))
- setTraceEndTime(attributes.value("traceEnd").
- toString().toLongLong());
- }
-
- if (elementName == "range") {
- QmlEventStartTime rangedEvent;
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("startTime"))
- rangedEvent.startTime = attributes.value("startTime").
- toString().toLongLong();
- if (attributes.hasAttribute("duration"))
- rangedEvent.length = attributes.value("duration").
- toString().toLongLong();
- if (attributes.hasAttribute("framerate"))
- rangedEvent.frameRate = attributes.value("framerate").
- toString().toInt();
- if (attributes.hasAttribute("animationcount"))
- rangedEvent.animationCount = attributes.value("animationcount").
- toString().toInt();
- else
- rangedEvent.animationCount = -1;
- if (attributes.hasAttribute("eventIndex")) {
- int ndx = attributes.value("eventIndex").toString().toInt();
- if (!descriptionBuffer.value(ndx))
- descriptionBuffer[ndx] = new QmlEvent;
- rangedEvent.description = descriptionBuffer.value(ndx);
- }
- rangedEvent.endTimeIndex = d->m_endTimeSortedList.length();
-
- if (!d->m_startTimeSortedList.isEmpty()
- && rangedEvent.startTime <
- d->m_startTimeSortedList.last().startTime)
- startTimesAreSorted = false;
- d->m_startTimeSortedList << rangedEvent;
-
- QmlEventEndTime endTimeEvent;
- endTimeEvent.endTime = rangedEvent.startTime + rangedEvent.length;
- endTimeEvent.startTimeIndex = d->m_startTimeSortedList.length()-1;
- endTimeEvent.description = rangedEvent.description;
- d->m_endTimeSortedList << endTimeEvent;
- break;
- }
-
- if (readingQmlEvents) {
- if (elementName == "event") {
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("index")) {
- int ndx = attributes.value("index").toString().toInt();
- if (!descriptionBuffer.value(ndx))
- descriptionBuffer[ndx] = new QmlEvent;
- currentEvent = descriptionBuffer[ndx];
- } else {
- currentEvent = 0;
- }
- break;
- }
-
- // the remaining are eventdata or v8eventdata elements
- if (!currentEvent)
- break;
-
- stream.readNext();
- if (stream.tokenType() != QXmlStreamReader::Characters)
- break;
- QString readData = stream.text().toString();
-
- if (elementName == "displayname") {
- currentEvent->displayname = readData;
- break;
- }
- if (elementName == "type") {
- currentEvent->eventType = qmlEventType(readData);
- break;
- }
- if (elementName == "filename") {
- currentEvent->location.filename = readData;
- break;
- }
- if (elementName == "line") {
- currentEvent->location.line = readData.toInt();
- break;
- }
- if (elementName == "column") {
- currentEvent->location.column = readData.toInt();
- }
- if (elementName == "details") {
- currentEvent->details = readData;
- break;
- }
- }
-
- if (readingV8Events) {
- if (elementName == "event") {
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute("index")) {
- int ndx = attributes.value("index").toString().toInt();
- if (!v8eventBuffer.value(ndx))
- v8eventBuffer[ndx] = new V8Event;
- v8event = v8eventBuffer[ndx];
- } else {
- v8event = 0;
- }
- break;
- }
-
- // the remaining are eventdata or v8eventdata elements
- if (!v8event)
- break;
-
- if (elementName == "childrenEvents") {
- QXmlStreamAttributes attributes = stream.attributes();
- int eventIndex = v8eventBuffer.key(v8event);
- if (attributes.hasAttribute("list")) {
- // store for later parsing (we haven't read all the events yet)
- childrenIndexes[eventIndex] =
- attributes.value("list").toString();
- }
- if (attributes.hasAttribute("childrenTimes")) {
- childrenTimes[eventIndex] =
- attributes.value("childrenTimes").toString();
- }
- if (attributes.hasAttribute("parentTimes")) {
- parentTimes[eventIndex] =
- attributes.value("parentTimes").toString();
- }
- }
-
- stream.readNext();
- if (stream.tokenType() != QXmlStreamReader::Characters)
- break;
- QString readData = stream.text().toString();
-
- if (elementName == "displayname") {
- v8event->displayName = readData;
- break;
- }
-
- if (elementName == "functionname") {
- v8event->functionName = readData;
- break;
- }
-
- if (elementName == "filename") {
- v8event->filename = readData;
- break;
- }
-
- if (elementName == "line") {
- v8event->line = readData.toInt();
- break;
- }
-
- if (elementName == "totalTime") {
- v8event->totalTime = readData.toDouble();
- cumulatedV8Time += v8event->totalTime;
- break;
- }
-
- if (elementName == "selfTime") {
- v8event->selfTime = readData.toDouble();
- break;
- }
- }
-
- break;
- }
- case QXmlStreamReader::EndElement : {
- if (elementName == "event") {
- currentEvent = 0;
- break;
- }
- if (elementName == "eventData") {
- readingQmlEvents = false;
- break;
- }
- if (elementName == "v8profile") {
- readingV8Events = false;
- }
- }
- default: break;
- }
- }
-
- file.close();
-
- if (stream.hasError()) {
- emit error(tr("Error while parsing %1").arg(filename));
- clear();
- return;
- }
-
- stream.clear();
-
- if (!validVersion) {
- clear();
- emit countChanged();
- emit dataReady();
- emit error(tr("Invalid version of QML Trace file."));
- return;
- }
-
- // backwards compatibility
- if (d->m_v8MeasuredTime == 0)
- d->m_v8MeasuredTime = cumulatedV8Time;
-
- // move the buffered data to the details cache
- foreach (QmlEvent *desc, descriptionBuffer.values()) {
- desc->eventHashStr = getHashStringForQmlEvent(
- desc->location, desc->eventType);;
- d->m_eventDescriptions[desc->eventHashStr] = desc;
- }
-
- // sort startTimeSortedList
- if (!startTimesAreSorted) {
- qSort(d->m_startTimeSortedList.begin(),
- d->m_startTimeSortedList.end(), compareStartTimes);
- for (int i = 0; i< d->m_startTimeSortedList.length(); i++) {
- QmlEventStartTime startTimeData = d->m_startTimeSortedList[i];
- d->m_endTimeSortedList[startTimeData.endTimeIndex].startTimeIndex = i;
- }
- qSort(d->m_endTimeSortedList.begin(),
- d->m_endTimeSortedList.end(), compareStartIndexes);
- }
-
- // find v8events' children and parents
- foreach (int parentIndex, childrenIndexes.keys()) {
- QStringList childrenStrings =
- childrenIndexes.value(parentIndex).split(",");
- QStringList childrenTimesStrings =
- childrenTimes.value(parentIndex).split(", ");
- QStringList parentTimesStrings =
- parentTimes.value(parentIndex).split(", ");
- for (int ndx = 0; ndx < childrenStrings.count(); ndx++) {
- int childIndex = childrenStrings[ndx].toInt();
- if (v8eventBuffer.value(childIndex)) {
- V8EventSub *newChild = new V8EventSub(v8eventBuffer[childIndex]);
- V8EventSub *newParent = new V8EventSub(v8eventBuffer[parentIndex]);
- if (childrenTimesStrings.count() > ndx)
- newChild->totalTime = childrenTimesStrings[ndx].toDouble();
- if (parentTimesStrings.count() > ndx)
- newParent->totalTime = parentTimesStrings[ndx].toDouble();
- v8eventBuffer[parentIndex]->childrenHash.insert(
- newChild->reference->displayName, newChild);
- v8eventBuffer[childIndex]->parentHash.insert(
- newParent->reference->displayName, newParent);
- }
- }
- }
- // store v8 events
- d->m_v8EventList = v8eventBuffer.values();
-
- emit countChanged();
-
- descriptionBuffer.clear();
-
- emit postProcessing();
- d->collectV8Statistics();
- postProcess();
-}
-
-///////////////////////////////////////////////
-qint64 ProfileData::getStartTime(int index) const
-{
- return d->m_startTimeSortedList[index].startTime;
-}
-
-qint64 ProfileData::getEndTime(int index) const
-{
- return d->m_startTimeSortedList[index].startTime +
- d->m_startTimeSortedList[index].length;
-}
-
-qint64 ProfileData::getDuration(int index) const
-{
- return d->m_startTimeSortedList[index].length;
-}
-
-int ProfileData::getType(int index) const
-{
- return d->m_startTimeSortedList[index].description->eventType;
-}
-
-int ProfileData::getNestingLevel(int index) const
-{
- return d->m_startTimeSortedList[index].nestingLevel;
-}
-
-int ProfileData::getNestingDepth(int index) const
-{
- return d->m_startTimeSortedList[index].nestingDepth;
-}
-
-QString ProfileData::getFilename(int index) const
-{
- return d->m_startTimeSortedList[index].description->location.filename;
-}
-
-int ProfileData::getLine(int index) const
-{
- return d->m_startTimeSortedList[index].description->location.line;
-}
-
-int ProfileData::getColumn(int index) const
-{
- return d->m_startTimeSortedList[index].description->location.column;
-}
-
-QString ProfileData::getDetails(int index) const
-{
- // special: animations
- if (d->m_startTimeSortedList[index].description->eventType ==
- QQmlProfilerService::Painting &&
- d->m_startTimeSortedList[index].animationCount >= 0)
- return tr("%1 animations at %2 FPS").arg(
- QString::number(d->m_startTimeSortedList[index].animationCount),
- QString::number(d->m_startTimeSortedList[index].frameRate));
- return d->m_startTimeSortedList[index].description->details;
-}
-
-int ProfileData::getEventId(int index) const {
- return d->m_startTimeSortedList[index].description->eventId;
-}
-
-int ProfileData::getFramerate(int index) const
-{
- return d->m_startTimeSortedList[index].frameRate;
-}
-
-int ProfileData::getAnimationCount(int index) const
-{
- return d->m_startTimeSortedList[index].animationCount;
-}
-
-int ProfileData::getMaximumAnimationCount() const
-{
- return d->m_maximumAnimationCount;
-}
-
-int ProfileData::getMinimumAnimationCount() const
-{
- return d->m_minimumAnimationCount;
-}
-
-int ProfileData::uniqueEventsOfType(int type) const
-{
- if (!d->m_typeCounts.contains(type))
- return 0;
- return d->m_typeCounts[type]->eventIds.count();
-}
-
-int ProfileData::maxNestingForType(int type) const
-{
- if (!d->m_typeCounts.contains(type))
- return 0;
- return d->m_typeCounts[type]->nestingCount;
-}
-
-QString ProfileData::eventTextForType(int type, int index) const
-{
- if (!d->m_typeCounts.contains(type))
- return QString();
- return d->m_eventDescriptions.values().at(
- d->m_typeCounts[type]->eventIds[index])->details;
-}
-
-QString ProfileData::eventDisplayNameForType(int type, int index) const
-{
- if (!d->m_typeCounts.contains(type))
- return QString();
- return d->m_eventDescriptions.values().at(
- d->m_typeCounts[type]->eventIds[index])->displayname;
-}
-
-int ProfileData::eventIdForType(int type, int index) const
-{
- if (!d->m_typeCounts.contains(type))
- return -1;
- return d->m_typeCounts[type]->eventIds[index];
-}
-
-int ProfileData::eventPosInType(int index) const
-{
- int eventType = d->m_startTimeSortedList[index].description->eventType;
- return d->m_typeCounts[eventType]->eventIds.indexOf(
- d->m_startTimeSortedList[index].description->eventId);
-}
diff --git a/tools/qmlprofiler/profiledata.h b/tools/qmlprofiler/profiledata.h
deleted file mode 100644
index f5d726916c..0000000000
--- a/tools/qmlprofiler/profiledata.h
+++ /dev/null
@@ -1,247 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** GNU Lesser General Public License Usage
-** 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, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU General
-** Public License version 3.0 as published by the Free Software Foundation
-** and appearing in the file LICENSE.GPL included in the packaging of this
-** file. Please review the following information to ensure the GNU General
-** Public License version 3.0 requirements will be met:
-** http://www.gnu.org/copyleft/gpl.html.
-**
-** Other Usage
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef PROFILEDATA_H
-#define PROFILEDATA_H
-
-#include <QtQml/private/qqmlprofilerservice_p.h>
-
-#include <QtCore/QObject>
-#include <QtCore/QHash>
-
-struct QmlEvent;
-struct V8Event;
-
-typedef QHash<QString, QmlEvent *> QmlEventHash;
-typedef QList<QmlEvent *> QmlEvents;
-typedef QList<V8Event *> V8Events;
-
-struct EventLocation
-{
- EventLocation() : line(-1),column(-1) {}
- EventLocation(const QString &file, int lineNumber, int columnNumber)
- : filename(file), line(lineNumber), column(columnNumber) {}
- QString filename;
- int line;
- int column;
-};
-
-struct QmlEventSub {
- QmlEventSub(QmlEvent *from)
- : reference(from), duration(0), calls(0), inLoopPath(false)
- {}
- QmlEventSub(QmlEventSub *from)
- : reference(from->reference), duration(from->duration),
- calls(from->calls), inLoopPath(from->inLoopPath)
- {}
- QmlEvent *reference;
- qint64 duration;
- qint64 calls;
- bool inLoopPath;
-};
-
-struct QmlEvent
-{
- QmlEvent();
- ~QmlEvent();
-
- QString displayname;
- QString eventHashStr;
- QString details;
- EventLocation location;
- QQmlProfilerService::RangeType eventType;
- QHash <QString, QmlEventSub *> parentHash;
- QHash <QString, QmlEventSub *> childrenHash;
- qint64 duration;
- qint64 calls;
- qint64 minTime;
- qint64 maxTime;
- double timePerCall;
- double percentOfTime;
- qint64 medianTime;
- int eventId;
- bool isBindingLoop;
-
- QmlEvent &operator=(const QmlEvent &ref);
-};
-
-struct V8EventSub {
- V8EventSub(V8Event *from)
- : reference(from), totalTime(0)
- {}
- V8EventSub(V8EventSub *from)
- : reference(from->reference), totalTime(from->totalTime)
- {}
-
- V8Event *reference;
- qint64 totalTime;
-};
-
-struct V8Event
-{
- V8Event();
- ~V8Event();
-
- QString displayName;
- QString filename;
- QString functionName;
- int line;
- double totalTime; // given in milliseconds
- double totalPercent;
- double selfTime;
- double selfPercent;
- QHash <QString, V8EventSub *> parentHash;
- QHash <QString, V8EventSub *> childrenHash;
- int eventId;
-
- V8Event &operator=(const V8Event &ref);
-};
-
-class ProfileData : public QObject
-{
- Q_OBJECT
-
-public:
- explicit ProfileData(QObject *parent = 0);
- ~ProfileData();
-
- QmlEvents getQmlEvents() const;
- QmlEvent *qmlEvent(int eventId) const;
- const V8Events& getV8Events() const;
- V8Event *v8Event(int eventId) const;
-
- int findFirstIndex(qint64 startTime) const;
- int findFirstIndexNoParents(qint64 startTime) const;
- int findLastIndex(qint64 endTime) const;
- Q_INVOKABLE qint64 firstTimeMark() const;
- Q_INVOKABLE qint64 lastTimeMark() const;
-
- Q_INVOKABLE int count() const;
-
- // data access
- Q_INVOKABLE qint64 getStartTime(int index) const;
- Q_INVOKABLE qint64 getEndTime(int index) const;
- Q_INVOKABLE qint64 getDuration(int index) const;
- Q_INVOKABLE int getType(int index) const;
- Q_INVOKABLE int getNestingLevel(int index) const;
- Q_INVOKABLE int getNestingDepth(int index) const;
- Q_INVOKABLE QString getFilename(int index) const;
- Q_INVOKABLE int getLine(int index) const;
- Q_INVOKABLE int getColumn(int index) const;
- Q_INVOKABLE QString getDetails(int index) const;
- Q_INVOKABLE int getEventId(int index) const;
- Q_INVOKABLE int getFramerate(int index) const;
- Q_INVOKABLE int getAnimationCount(int index) const;
- Q_INVOKABLE int getMaximumAnimationCount() const;
- Q_INVOKABLE int getMinimumAnimationCount() const;
-
- // per-type data
- Q_INVOKABLE int uniqueEventsOfType(int type) const;
- Q_INVOKABLE int maxNestingForType(int type) const;
- Q_INVOKABLE QString eventTextForType(int type, int index) const;
- Q_INVOKABLE QString eventDisplayNameForType(int type, int index) const;
- Q_INVOKABLE int eventIdForType(int type, int index) const;
- Q_INVOKABLE int eventPosInType(int index) const;
-
- Q_INVOKABLE qint64 traceStartTime() const;
- Q_INVOKABLE qint64 traceEndTime() const;
- Q_INVOKABLE qint64 traceDuration() const;
- Q_INVOKABLE qint64 qmlMeasuredTime() const;
- Q_INVOKABLE qint64 v8MeasuredTime() const;
-
- void showErrorDialog(const QString &st ) const;
- void compileStatistics(qint64 startTime, qint64 endTime);
-
-signals:
- void dataReady();
- void countChanged();
- void error(const QString &error);
- void dataClear();
- void processingData();
- void postProcessing();
-
- void requestDetailsForLocation(int eventType, const EventLocation &location);
- void detailsChanged(int eventId, const QString &newString);
- void reloadDetailLabels();
- void reloadDocumentsForDetails();
-
-public slots:
- void clear();
- void addQmlEvent(QQmlProfilerService::RangeType type,
- qint64 startTime, qint64 length,
- const QStringList &data,
- const EventLocation &location);
- void complete();
-
- void addV8Event(int depth,const QString &function,const QString &filename,
- int lineNumber, double totalTime, double selfTime);
- void addFrameEvent(qint64 time, int framerate, int animationcount);
- bool save(const QString &filename);
- void load(const QString &filename);
- void setFilename(const QString &filename);
- void load();
-
- void setTraceEndTime( qint64 time );
- void setTraceStartTime( qint64 time );
-
- void rewriteDetailsString(QQmlProfilerService::RangeType eventType,
- const EventLocation &location,
- const QString &newString);
- void finishedRewritingDetails();
-
-private:
- void postProcess();
- void sortEndTimes();
- void findAnimationLimits();
- void sortStartTimes();
- void computeLevels();
- void computeNestingLevels();
- void computeNestingDepth();
- void prepareForDisplay();
- void linkEndsToStarts();
- void reloadDetails();
- void findBindingLoops(qint64 startTime, qint64 endTime);
-
-private:
- class ProfileDataPrivate *d;
-};
-
-#endif // PROFILEDATA_H
diff --git a/tools/qmlprofiler/qmlprofiler.pro b/tools/qmlprofiler/qmlprofiler.pro
index db7e357b70..c35c487a1d 100644
--- a/tools/qmlprofiler/qmlprofiler.pro
+++ b/tools/qmlprofiler/qmlprofiler.pro
@@ -14,14 +14,15 @@ CONFIG += console declarative_debug
SOURCES += main.cpp \
qmlprofilerapplication.cpp \
commandlistener.cpp \
- profileclient.cpp \
- profiledata.cpp \
- qqmldebugclient.cpp
+ qqmldebugclient.cpp \
+ qmlprofilerdata.cpp \
+ qmlprofilerclient.cpp
HEADERS += \
qmlprofilerapplication.h \
commandlistener.h \
constants.h \
- profileclient.h \
- profiledata.h \
+ qmlprofilerdata.h \
+ qmlprofilerclient.h \
+ qmlprofilereventlocation.h \
qqmldebugclient.h
diff --git a/tools/qmlprofiler/qmlprofilerapplication.cpp b/tools/qmlprofiler/qmlprofilerapplication.cpp
index 0b10ec0b15..05518dd167 100644
--- a/tools/qmlprofiler/qmlprofilerapplication.cpp
+++ b/tools/qmlprofiler/qmlprofilerapplication.cpp
@@ -104,20 +104,20 @@ QmlProfilerApplication::QmlProfilerApplication(int &argc, char **argv) :
connect(&m_qmlProfilerClient, SIGNAL(enabledChanged()), this, SLOT(traceClientEnabled()));
connect(&m_qmlProfilerClient, SIGNAL(recordingChanged(bool)), this, SLOT(recordingChanged()));
- connect(&m_qmlProfilerClient, SIGNAL(range(QQmlProfilerService::RangeType,qint64,qint64,QStringList,EventLocation)),
- &m_profileData, SLOT(addQmlEvent(QQmlProfilerService::RangeType,qint64,qint64,QStringList,EventLocation)));
- connect(&m_qmlProfilerClient, SIGNAL(traceFinished(qint64)), &m_profileData, SLOT(setTraceEndTime(qint64)));
- connect(&m_qmlProfilerClient, SIGNAL(traceStarted(qint64)), &m_profileData, SLOT(setTraceStartTime(qint64)));
- connect(&m_qmlProfilerClient, SIGNAL(frame(qint64,int,int)), &m_profileData, SLOT(addFrameEvent(qint64,int,int)));
+ connect(&m_qmlProfilerClient, SIGNAL(range(QQmlProfilerService::RangeType,qint64,qint64,QStringList,QmlEventLocation)),
+ &m_profilerData, SLOT(addQmlEvent(QQmlProfilerService::RangeType,qint64,qint64,QStringList,QmlEventLocation)));
+ connect(&m_qmlProfilerClient, SIGNAL(traceFinished(qint64)), &m_profilerData, SLOT(setTraceEndTime(qint64)));
+ connect(&m_qmlProfilerClient, SIGNAL(traceStarted(qint64)), &m_profilerData, SLOT(setTraceStartTime(qint64)));
+ connect(&m_qmlProfilerClient, SIGNAL(frame(qint64,int,int)), &m_profilerData, SLOT(addFrameEvent(qint64,int,int)));
connect(&m_qmlProfilerClient, SIGNAL(complete()), this, SLOT(qmlComplete()));
connect(&m_v8profilerClient, SIGNAL(enabledChanged()), this, SLOT(profilerClientEnabled()));
connect(&m_v8profilerClient, SIGNAL(range(int,QString,QString,int,double,double)),
- &m_profileData, SLOT(addV8Event(int,QString,QString,int,double,double)));
+ &m_profilerData, SLOT(addV8Event(int,QString,QString,int,double,double)));
connect(&m_v8profilerClient, SIGNAL(complete()), this, SLOT(v8Complete()));
- connect(&m_profileData, SIGNAL(error(QString)), this, SLOT(logError(QString)));
- connect(&m_profileData, SIGNAL(dataReady()), this, SLOT(traceFinished()));
+ connect(&m_profilerData, SIGNAL(error(QString)), this, SLOT(logError(QString)));
+ connect(&m_profilerData, SIGNAL(dataReady()), this, SLOT(traceFinished()));
}
@@ -375,7 +375,7 @@ void QmlProfilerApplication::traceFinished()
{
const QString fileName = traceFileName();
- if (m_profileData.save(fileName))
+ if (m_profilerData.save(fileName))
print(QString("Saving trace to %1.").arg(fileName));
if (m_quitAfterSave)
@@ -416,7 +416,7 @@ void QmlProfilerApplication::qmlComplete()
m_qmlDataReady = true;
if (m_v8profilerClient.state() != QQmlDebugClient::Enabled ||
m_v8DataReady) {
- m_profileData.complete();
+ m_profilerData.complete();
// once complete is sent, reset the flag
m_qmlDataReady = false;
}
@@ -427,7 +427,7 @@ void QmlProfilerApplication::v8Complete()
m_v8DataReady = true;
if (m_qmlProfilerClient.state() != QQmlDebugClient::Enabled ||
m_qmlDataReady) {
- m_profileData.complete();
+ m_profilerData.complete();
// once complete is sent, reset the flag
m_v8DataReady = false;
}
diff --git a/tools/qmlprofiler/qmlprofilerapplication.h b/tools/qmlprofiler/qmlprofilerapplication.h
index 3937db7e5f..c7148880ad 100644
--- a/tools/qmlprofiler/qmlprofilerapplication.h
+++ b/tools/qmlprofiler/qmlprofilerapplication.h
@@ -46,7 +46,8 @@
#include <QtCore/QProcess>
#include <QtCore/QTimer>
-#include "profileclient.h"
+#include "qmlprofilerclient.h"
+#include "qmlprofilerdata.h"
class QmlProfilerApplication : public QCoreApplication
{
@@ -104,9 +105,9 @@ private:
bool m_quitAfterSave;
QQmlDebugConnection m_connection;
- QmlProfileClient m_qmlProfilerClient;
- V8ProfileClient m_v8profilerClient;
- ProfileData m_profileData;
+ QmlProfilerClient m_qmlProfilerClient;
+ V8ProfilerClient m_v8profilerClient;
+ QmlProfilerData m_profilerData;
QTimer m_connectTimer;
uint m_connectionAttempts;
diff --git a/tools/qmlprofiler/profileclient.cpp b/tools/qmlprofiler/qmlprofilerclient.cpp
index 85287464cf..97ed90e890 100644
--- a/tools/qmlprofiler/profileclient.cpp
+++ b/tools/qmlprofiler/qmlprofilerclient.cpp
@@ -39,12 +39,12 @@
**
****************************************************************************/
-#include "profileclient.h"
+#include "qmlprofilerclient.h"
#include <QtCore/QStack>
#include <QtCore/QStringList>
-ProfileClient::ProfileClient(const QString &clientName,
+ProfilerClient::ProfilerClient(const QString &clientName,
QQmlDebugConnection *client)
: QQmlDebugClient(clientName, client),
m_recording(false),
@@ -52,7 +52,7 @@ ProfileClient::ProfileClient(const QString &clientName,
{
}
-ProfileClient::~ProfileClient()
+ProfilerClient::~ProfilerClient()
{
//Disable profiling if started by client
//Profiling data will be lost!!
@@ -60,26 +60,26 @@ ProfileClient::~ProfileClient()
setRecording(false);
}
-void ProfileClient::clearData()
+void ProfilerClient::clearData()
{
emit cleared();
}
-bool ProfileClient::isEnabled() const
+bool ProfilerClient::isEnabled() const
{
return m_enabled;
}
-void ProfileClient::sendRecordingStatus()
+void ProfilerClient::sendRecordingStatus()
{
}
-bool ProfileClient::isRecording() const
+bool ProfilerClient::isRecording() const
{
return m_recording;
}
-void ProfileClient::setRecording(bool v)
+void ProfilerClient::setRecording(bool v)
{
if (v == m_recording)
return;
@@ -93,7 +93,7 @@ void ProfileClient::setRecording(bool v)
emit recordingChanged(v);
}
-void ProfileClient::stateChanged(State status)
+void ProfilerClient::stateChanged(State status)
{
if ((m_enabled && status != Enabled) ||
(!m_enabled && status == Enabled))
@@ -103,10 +103,10 @@ void ProfileClient::stateChanged(State status)
}
-class QmlProfileClientPrivate
+class QmlProfilerClientPrivate
{
public:
- QmlProfileClientPrivate()
+ QmlProfilerClientPrivate()
: inProgressRanges(0)
, maximumTime(0)
{
@@ -117,31 +117,31 @@ public:
qint64 inProgressRanges;
QStack<qint64> rangeStartTimes[QQmlProfilerService::MaximumRangeType];
QStack<QStringList> rangeDatas[QQmlProfilerService::MaximumRangeType];
- QStack<EventLocation> rangeLocations[QQmlProfilerService::MaximumRangeType];
+ QStack<QmlEventLocation> rangeLocations[QQmlProfilerService::MaximumRangeType];
int rangeCount[QQmlProfilerService::MaximumRangeType];
qint64 maximumTime;
};
-QmlProfileClient::QmlProfileClient(
+QmlProfilerClient::QmlProfilerClient(
QQmlDebugConnection *client)
- : ProfileClient(QLatin1String("CanvasFrameRate"), client),
- d(new QmlProfileClientPrivate)
+ : ProfilerClient(QStringLiteral("CanvasFrameRate"), client),
+ d(new QmlProfilerClientPrivate)
{
}
-QmlProfileClient::~QmlProfileClient()
+QmlProfilerClient::~QmlProfilerClient()
{
delete d;
}
-void QmlProfileClient::clearData()
+void QmlProfilerClient::clearData()
{
::memset(d->rangeCount, 0,
QQmlProfilerService::MaximumRangeType * sizeof(int));
- ProfileClient::clearData();
+ ProfilerClient::clearData();
}
-void QmlProfileClient::sendRecordingStatus()
+void QmlProfilerClient::sendRecordingStatus()
{
QByteArray ba;
QDataStream stream(&ba, QIODevice::WriteOnly);
@@ -149,7 +149,7 @@ void QmlProfileClient::sendRecordingStatus()
sendMessage(ba);
}
-void QmlProfileClient::messageReceived(const QByteArray &data)
+void QmlProfilerClient::messageReceived(const QByteArray &data)
{
QByteArray rwData = data;
QDataStream stream(&rwData, QIODevice::ReadOnly);
@@ -216,7 +216,7 @@ void QmlProfileClient::messageReceived(const QByteArray &data)
stream >> column;
if (d->rangeCount[range] > 0) {
- d->rangeLocations[range].push(EventLocation(fileName, line,
+ d->rangeLocations[range].push(QmlEventLocation(fileName, line,
column));
}
} else {
@@ -228,8 +228,8 @@ void QmlProfileClient::messageReceived(const QByteArray &data)
d->maximumTime = qMax(time, d->maximumTime);
QStringList data = d->rangeDatas[range].count() ?
d->rangeDatas[range].pop() : QStringList();
- EventLocation location = d->rangeLocations[range].count() ?
- d->rangeLocations[range].pop() : EventLocation();
+ QmlEventLocation location = d->rangeLocations[range].count() ?
+ d->rangeLocations[range].pop() : QmlEventLocation();
qint64 startTime = d->rangeStartTimes[range].pop();
emit this->range((QQmlProfilerService::RangeType)range,
@@ -246,16 +246,16 @@ void QmlProfileClient::messageReceived(const QByteArray &data)
}
}
-V8ProfileClient::V8ProfileClient(QQmlDebugConnection *client)
- : ProfileClient(QLatin1String("V8Profiler"), client)
+V8ProfilerClient::V8ProfilerClient(QQmlDebugConnection *client)
+ : ProfilerClient(QStringLiteral("V8Profiler"), client)
{
}
-V8ProfileClient::~V8ProfileClient()
+V8ProfilerClient::~V8ProfilerClient()
{
}
-void V8ProfileClient::sendRecordingStatus()
+void V8ProfilerClient::sendRecordingStatus()
{
QByteArray ba;
QDataStream stream(&ba, QIODevice::WriteOnly);
@@ -272,7 +272,7 @@ void V8ProfileClient::sendRecordingStatus()
sendMessage(ba);
}
-void V8ProfileClient::messageReceived(const QByteArray &data)
+void V8ProfilerClient::messageReceived(const QByteArray &data)
{
QByteArray rwData = data;
QDataStream stream(&rwData, QIODevice::ReadOnly);
diff --git a/tools/qmlprofiler/profileclient.h b/tools/qmlprofiler/qmlprofilerclient.h
index 54826a291a..2a8629d1fb 100644
--- a/tools/qmlprofiler/profileclient.h
+++ b/tools/qmlprofiler/qmlprofilerclient.h
@@ -39,16 +39,15 @@
**
****************************************************************************/
-#ifndef PROFILECLIENT_H
-#define PROFILECLIENT_H
-
-#include "profiledata.h"
+#ifndef QMLPROFILERCLIENT_H
+#define QMLPROFILERCLIENT_H
#include "qqmldebugclient.h"
#include <QtQml/private/qqmlprofilerservice_p.h>
+#include "qmlprofilereventlocation.h"
-class ProfileClientPrivate;
-class ProfileClient : public QQmlDebugClient
+class ProfilerClientPrivate;
+class ProfilerClient : public QQmlDebugClient
{
Q_OBJECT
@@ -57,9 +56,9 @@ class ProfileClient : public QQmlDebugClient
NOTIFY recordingChanged)
public:
- ProfileClient(const QString & clientName,
+ ProfilerClient(const QString &clientName,
QQmlDebugConnection *client);
- ~ProfileClient();
+ ~ProfilerClient();
bool isEnabled() const;
bool isRecording() const;
@@ -83,13 +82,13 @@ protected:
bool m_enabled;
};
-class QmlProfileClient : public ProfileClient
+class QmlProfilerClient : public ProfilerClient
{
Q_OBJECT
public:
- QmlProfileClient(QQmlDebugConnection *client);
- ~QmlProfileClient();
+ QmlProfilerClient(QQmlDebugConnection *client);
+ ~QmlProfilerClient();
public slots:
void clearData();
@@ -100,17 +99,17 @@ signals:
void traceStarted( qint64 time );
void range(QQmlProfilerService::RangeType type, qint64 startTime,
qint64 length, const QStringList &data,
- const EventLocation &location);
+ const QmlEventLocation &location);
void frame(qint64 time, int frameRate, int animationCount);
protected:
virtual void messageReceived(const QByteArray &);
private:
- class QmlProfileClientPrivate *d;
+ class QmlProfilerClientPrivate *d;
};
-class V8ProfileClient : public ProfileClient
+class V8ProfilerClient : public ProfilerClient
{
Q_OBJECT
@@ -122,8 +121,8 @@ public:
V8MaximumMessage
};
- V8ProfileClient(QQmlDebugConnection *client);
- ~V8ProfileClient();
+ V8ProfilerClient(QQmlDebugConnection *client);
+ ~V8ProfilerClient();
public slots:
void sendRecordingStatus();
@@ -136,4 +135,4 @@ protected:
virtual void messageReceived(const QByteArray &);
};
-#endif // PROFILECLIENT_H
+#endif // QMLPROFILERCLIENT_H
diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp
new file mode 100644
index 0000000000..38e64ff46e
--- /dev/null
+++ b/tools/qmlprofiler/qmlprofilerdata.cpp
@@ -0,0 +1,602 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmlprofilerdata.h"
+
+#include <QStringList>
+#include <QUrl>
+#include <QHash>
+#include <QFile>
+#include <QXmlStreamReader>
+
+namespace Constants {
+ const char TYPE_PAINTING_STR[] = "Painting";
+ const char TYPE_COMPILING_STR[] = "Compiling";
+ const char TYPE_CREATING_STR[] = "Creating";
+ const char TYPE_BINDING_STR[] = "Binding";
+ const char TYPE_HANDLINGSIGNAL_STR[] = "HandlingSignal";
+ const char PROFILER_FILE_VERSION[] = "1.02";
+}
+
+struct QmlRangeEventData {
+ QmlRangeEventData() {} // never called
+ QmlRangeEventData(const QString &_displayName,
+ const QString &_eventHashStr,
+ const QmlEventLocation &_location,
+ const QString &_details,
+ const QQmlProfilerService::RangeType &_eventType)
+ : displayName(_displayName),eventHashStr(_eventHashStr),location(_location),
+ details(_details),eventType(_eventType) {}
+ QString displayName;
+ QString eventHashStr;
+ QmlEventLocation location;
+ QString details;
+ QQmlProfilerService::RangeType eventType;
+};
+
+struct QmlRangeEventStartInstance {
+ QmlRangeEventStartInstance() {} // never called
+ QmlRangeEventStartInstance(qint64 _startTime, qint64 _duration, int _frameRate,
+ int _animationCount, QmlRangeEventData *_data)
+ : startTime(_startTime), duration(_duration), frameRate(_frameRate),
+ animationCount(_animationCount), data(_data)
+ { }
+ qint64 startTime;
+ qint64 duration;
+ int frameRate;
+ int animationCount;
+ QmlRangeEventData *data;
+};
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_TYPEINFO(QmlRangeEventData, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QmlRangeEventStartInstance, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+struct QV8EventInfo {
+ QString displayName;
+ QString eventHashStr;
+ QString functionName;
+ QString fileName;
+ int line;
+ qint64 totalTime;
+ qint64 selfTime;
+
+ QHash<QString, qint64> v8children;
+};
+
+/////////////////////////////////////////////////////////////////
+class QmlProfilerDataPrivate
+{
+public:
+ QmlProfilerDataPrivate(QmlProfilerData *qq){ Q_UNUSED(qq); }
+
+ // data storage
+ QHash<QString, QmlRangeEventData *> eventDescriptions;
+ QVector<QmlRangeEventStartInstance> startInstanceList;
+ QHash<QString, QV8EventInfo *> v8EventHash;
+
+ qint64 traceStartTime;
+ qint64 traceEndTime;
+
+ // internal state while collecting events
+ QmlRangeEventStartInstance *lastFrameEvent;
+ qint64 qmlMeasuredTime;
+ qint64 v8MeasuredTime;
+ QHash<int, QV8EventInfo *> v8parents;
+ void clearV8RootEvent();
+ QV8EventInfo v8RootEvent;
+
+ QmlProfilerData::State state;
+};
+
+/////////////////////////////////////////////////////////////////
+QmlProfilerData::QmlProfilerData(QObject *parent) :
+ QObject(parent),d(new QmlProfilerDataPrivate(this))
+{
+ d->state = Empty;
+ clear();
+}
+
+QmlProfilerData::~QmlProfilerData()
+{
+ clear();
+ delete d;
+}
+
+void QmlProfilerData::clear()
+{
+ qDeleteAll(d->eventDescriptions.values());
+ d->eventDescriptions.clear();
+ d->startInstanceList.clear();
+
+ qDeleteAll(d->v8EventHash.values());
+ d->v8EventHash.clear();
+ d->v8parents.clear();
+ d->clearV8RootEvent();
+ d->v8MeasuredTime = 0;
+
+ d->traceEndTime = 0;
+ d->traceStartTime = -1;
+ d->qmlMeasuredTime = 0;
+
+ d->lastFrameEvent = 0;
+
+ setState(Empty);
+}
+
+QString QmlProfilerData::getHashStringForQmlEvent(const QmlEventLocation &location, int eventType)
+{
+ return QString(QStringLiteral("%1:%2:%3:%4")).arg(
+ location.filename,
+ QString::number(location.line),
+ QString::number(location.column),
+ QString::number(eventType));
+}
+
+QString QmlProfilerData::getHashStringForV8Event(const QString &displayName, const QString &function)
+{
+ return QString(QStringLiteral("%1:%2")).arg(displayName, function);
+}
+
+QString QmlProfilerData::qmlRangeTypeAsString(QQmlProfilerService::RangeType typeEnum)
+{
+ switch (typeEnum) {
+ case QQmlProfilerService::Painting:
+ return QLatin1String(Constants::TYPE_PAINTING_STR);
+ break;
+ case QQmlProfilerService::Compiling:
+ return QLatin1String(Constants::TYPE_COMPILING_STR);
+ break;
+ case QQmlProfilerService::Creating:
+ return QLatin1String(Constants::TYPE_CREATING_STR);
+ break;
+ case QQmlProfilerService::Binding:
+ return QLatin1String(Constants::TYPE_BINDING_STR);
+ break;
+ case QQmlProfilerService::HandlingSignal:
+ return QLatin1String(Constants::TYPE_HANDLINGSIGNAL_STR);
+ break;
+ default:
+ return QString::number((int)typeEnum);
+ }
+}
+
+void QmlProfilerData::setTraceStartTime(qint64 time)
+{
+ d->traceStartTime = time;
+}
+
+void QmlProfilerData::setTraceEndTime(qint64 time)
+{
+ d->traceEndTime = time;
+}
+
+qint64 QmlProfilerData::traceStartTime() const
+{
+ return d->traceStartTime;
+}
+
+qint64 QmlProfilerData::traceEndTime() const
+{
+ return d->traceEndTime;
+}
+
+void QmlProfilerData::addQmlEvent(QQmlProfilerService::RangeType type,
+ qint64 startTime,
+ qint64 duration,
+ const QStringList &data,
+ const QmlEventLocation &location)
+{
+ setState(AcquiringData);
+
+ QString details;
+ // generate details string
+ if (data.isEmpty())
+ details = tr("Source code not available");
+ else {
+ details = data.join(QStringLiteral(" ")).replace(
+ QLatin1Char('\n'),QStringLiteral(" ")).simplified();
+ QRegExp rewrite(QStringLiteral("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)"));
+ bool match = rewrite.exactMatch(details);
+ if (match) {
+ details = rewrite.cap(1) +QLatin1String(": ") + rewrite.cap(3);
+ }
+ if (details.startsWith(QLatin1String("file://")))
+ details = details.mid(details.lastIndexOf(QLatin1Char('/')) + 1);
+ }
+
+ QmlEventLocation eventLocation = location;
+ QString displayName, eventHashStr;
+ // generate hash
+ if (eventLocation.filename.isEmpty()) {
+ displayName = tr("<bytecode>");
+ eventHashStr = getHashStringForQmlEvent(eventLocation, type);
+ } else {
+ const QString filePath = QUrl(eventLocation.filename).path();
+ displayName = filePath.mid(
+ filePath.lastIndexOf(QLatin1Char('/')) + 1) +
+ QLatin1Char(':') + QString::number(eventLocation.line);
+ eventHashStr = getHashStringForQmlEvent(eventLocation, type);
+ }
+
+ QmlRangeEventData *newEvent;
+ if (d->eventDescriptions.contains(eventHashStr)) {
+ newEvent = d->eventDescriptions[eventHashStr];
+ } else {
+ newEvent = new QmlRangeEventData(displayName, eventHashStr, location, details, type);
+ d->eventDescriptions.insert(eventHashStr, newEvent);
+ }
+
+ QmlRangeEventStartInstance rangeEventStartInstance(startTime, duration, 1e9/duration, -1, newEvent);
+
+ d->startInstanceList.append(rangeEventStartInstance);
+}
+
+void QmlProfilerData::addFrameEvent(qint64 time, int framerate, int animationcount)
+{
+ setState(AcquiringData);
+
+ QString details = tr("Animation Timer Update");
+ QString displayName = tr("<Animation Update>");
+ QString eventHashStr = displayName;
+
+ QmlRangeEventData *newEvent;
+ if (d->eventDescriptions.contains(eventHashStr)) {
+ newEvent = d->eventDescriptions[eventHashStr];
+ } else {
+ newEvent = new QmlRangeEventData(displayName, eventHashStr, QmlEventLocation(), details, QQmlProfilerService::Painting);
+ d->eventDescriptions.insert(eventHashStr, newEvent);
+ }
+
+ qint64 duration = 1e9/framerate;
+ // avoid overlap
+ if (d->lastFrameEvent &&
+ d->lastFrameEvent->startTime + d->lastFrameEvent->duration >= time) {
+ d->lastFrameEvent->duration = time - 1 - d->lastFrameEvent->startTime;
+ }
+
+ QmlRangeEventStartInstance rangeEventStartInstance(time, duration, framerate, animationcount, newEvent);
+
+ d->startInstanceList.append(rangeEventStartInstance);
+
+ d->lastFrameEvent = &d->startInstanceList.last();
+}
+
+QString QmlProfilerData::rootEventName()
+{
+ return tr("<program>");
+}
+
+QString QmlProfilerData::rootEventDescription()
+{
+ return tr("Main Program");
+}
+
+void QmlProfilerDataPrivate::clearV8RootEvent()
+{
+ v8RootEvent.displayName = QmlProfilerData::rootEventName();
+ v8RootEvent.eventHashStr = QmlProfilerData::rootEventName();
+ v8RootEvent.functionName = QmlProfilerData::rootEventDescription();
+ v8RootEvent.line = -1;
+ v8RootEvent.totalTime = 0;
+ v8RootEvent.selfTime = 0;
+ v8RootEvent.v8children.clear();
+}
+
+void QmlProfilerData::addV8Event(int depth, const QString &function, const QString &filename,
+ int lineNumber, double totalTime, double selfTime)
+{
+ QString displayName = filename.mid(filename.lastIndexOf(QLatin1Char('/')) + 1) +
+ QLatin1Char(':') + QString::number(lineNumber);
+ QString hashStr = getHashStringForV8Event(displayName, function);
+
+ setState(AcquiringData);
+
+ // time is given in milliseconds, but internally we store it in microseconds
+ totalTime *= 1e6;
+ selfTime *= 1e6;
+
+ // accumulate information
+ QV8EventInfo *eventData = d->v8EventHash[hashStr];
+ if (!eventData) {
+ eventData = new QV8EventInfo;
+ eventData->displayName = displayName;
+ eventData->eventHashStr = hashStr;
+ eventData->fileName = filename;
+ eventData->functionName = function;
+ eventData->line = lineNumber;
+ eventData->totalTime = totalTime;
+ eventData->selfTime = selfTime;
+ d->v8EventHash[hashStr] = eventData;
+ } else {
+ eventData->totalTime += totalTime;
+ eventData->selfTime += selfTime;
+ }
+ d->v8parents[depth] = eventData;
+
+ QV8EventInfo *parentEvent = 0;
+ if (depth == 0) {
+ parentEvent = &d->v8RootEvent;
+ d->v8MeasuredTime += totalTime;
+ }
+ if (depth > 0 && d->v8parents.contains(depth-1)) {
+ parentEvent = d->v8parents.value(depth-1);
+ }
+
+ if (parentEvent != 0) {
+ if (!parentEvent->v8children.contains(eventData->eventHashStr)) {
+ parentEvent->v8children[eventData->eventHashStr] = totalTime;
+ } else {
+ parentEvent->v8children[eventData->eventHashStr] += totalTime;
+ }
+ }
+}
+
+void QmlProfilerData::computeQmlTime()
+{
+ // compute levels
+ QHash<int, qint64> endtimesPerLevel;
+ int minimumLevel = 1;
+ int level = minimumLevel;
+
+ for (int i = 0; i < d->startInstanceList.count(); i++) {
+ qint64 st = d->startInstanceList[i].startTime;
+ int type = d->startInstanceList[i].data->eventType;
+
+ if (type == QQmlProfilerService::Painting) {
+ continue;
+ }
+
+ // general level
+ if (endtimesPerLevel[level] > st) {
+ level++;
+ } else {
+ while (level > minimumLevel && endtimesPerLevel[level-1] <= st)
+ level--;
+ }
+ endtimesPerLevel[level] = st + d->startInstanceList[i].duration;
+
+ if (level == minimumLevel) {
+ d->qmlMeasuredTime += d->startInstanceList[i].duration;
+ }
+ }
+}
+
+bool compareStartTimes(const QmlRangeEventStartInstance &t1, const QmlRangeEventStartInstance &t2)
+{
+ return t1.startTime < t2.startTime;
+}
+
+void QmlProfilerData::sortStartTimes()
+{
+ if (d->startInstanceList.count() < 2)
+ return;
+
+ // assuming startTimes is partially sorted
+ // identify blocks of events and sort them with quicksort
+ QVector<QmlRangeEventStartInstance>::iterator itFrom = d->startInstanceList.end() - 2;
+ QVector<QmlRangeEventStartInstance>::iterator itTo = d->startInstanceList.end() - 1;
+
+ while (itFrom != d->startInstanceList.begin() && itTo != d->startInstanceList.begin()) {
+ // find block to sort
+ while ( itFrom != d->startInstanceList.begin()
+ && itTo->startTime > itFrom->startTime ) {
+ itTo--;
+ itFrom = itTo - 1;
+ }
+
+ // if we're at the end of the list
+ if (itFrom == d->startInstanceList.begin())
+ break;
+
+ // find block length
+ while ( itFrom != d->startInstanceList.begin()
+ && itTo->startTime <= itFrom->startTime )
+ itFrom--;
+
+ if (itTo->startTime <= itFrom->startTime)
+ qSort(itFrom, itTo + 1, compareStartTimes);
+ else
+ qSort(itFrom + 1, itTo + 1, compareStartTimes);
+
+ // move to next block
+ itTo = itFrom;
+ itFrom = itTo - 1;
+ }
+}
+
+void QmlProfilerData::complete()
+{
+ setState(ProcessingData);
+ sortStartTimes();
+ computeQmlTime();
+ setState(Done);
+ emit dataReady();
+}
+
+bool QmlProfilerData::isEmpty() const
+{
+ return d->startInstanceList.isEmpty() && d->v8EventHash.isEmpty();
+}
+
+bool QmlProfilerData::save(const QString &filename)
+{
+ if (isEmpty()) {
+ emit error(tr("No data to save"));
+ return false;
+ }
+
+ QFile file(filename);
+ if (!file.open(QIODevice::WriteOnly)) {
+ emit error(tr("Could not open %1 for writing").arg(filename));
+ return false;
+ }
+
+ QXmlStreamWriter stream(&file);
+ stream.setAutoFormatting(true);
+ stream.writeStartDocument();
+
+ stream.writeStartElement(QStringLiteral("trace"));
+ stream.writeAttribute(QStringLiteral("version"), QLatin1String(Constants::PROFILER_FILE_VERSION));
+
+ stream.writeAttribute(QStringLiteral("traceStart"), QString::number(traceStartTime()));
+ stream.writeAttribute(QStringLiteral("traceEnd"), QString::number(traceEndTime()));
+
+ stream.writeStartElement(QStringLiteral("eventData"));
+ stream.writeAttribute(QStringLiteral("totalTime"), QString::number(d->qmlMeasuredTime));
+
+ foreach (const QmlRangeEventData *eventData, d->eventDescriptions.values()) {
+ stream.writeStartElement(QStringLiteral("event"));
+ stream.writeAttribute(QStringLiteral("index"), QString::number(d->eventDescriptions.keys().indexOf(eventData->eventHashStr)));
+ stream.writeTextElement(QStringLiteral("displayname"), eventData->displayName);
+ stream.writeTextElement(QStringLiteral("type"), qmlRangeTypeAsString(eventData->eventType));
+ if (!eventData->location.filename.isEmpty()) {
+ stream.writeTextElement(QStringLiteral("filename"), eventData->location.filename);
+ stream.writeTextElement(QStringLiteral("line"), QString::number(eventData->location.line));
+ stream.writeTextElement(QStringLiteral("column"), QString::number(eventData->location.column));
+ }
+ stream.writeTextElement(QStringLiteral("details"), eventData->details);
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // eventData
+
+ stream.writeStartElement(QStringLiteral("profilerDataModel"));
+ foreach (const QmlRangeEventStartInstance &rangedEvent, d->startInstanceList) {
+ stream.writeStartElement(QStringLiteral("range"));
+ stream.writeAttribute(QStringLiteral("startTime"), QString::number(rangedEvent.startTime));
+ stream.writeAttribute(QStringLiteral("duration"), QString::number(rangedEvent.duration));
+ stream.writeAttribute(QStringLiteral("eventIndex"), QString::number(d->eventDescriptions.keys().indexOf(rangedEvent.data->eventHashStr)));
+ if (rangedEvent.data->eventType == QQmlProfilerService::Painting && rangedEvent.animationCount >= 0) {
+ // animation frame
+ stream.writeAttribute(QStringLiteral("framerate"), QString::number(rangedEvent.frameRate));
+ stream.writeAttribute(QStringLiteral("animationcount"), QString::number(rangedEvent.animationCount));
+ }
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // profilerDataModel
+
+ stream.writeStartElement(QStringLiteral("v8profile")); // v8 profiler output
+ stream.writeAttribute(QStringLiteral("totalTime"), QString::number(d->v8MeasuredTime));
+ foreach (QV8EventInfo *v8event, d->v8EventHash.values()) {
+ stream.writeStartElement(QStringLiteral("event"));
+ stream.writeAttribute(QStringLiteral("index"), QString::number(d->v8EventHash.keys().indexOf(v8event->eventHashStr)));
+ stream.writeTextElement(QStringLiteral("displayname"), v8event->displayName);
+ stream.writeTextElement(QStringLiteral("functionname"), v8event->functionName);
+ if (!v8event->fileName.isEmpty()) {
+ stream.writeTextElement(QStringLiteral("filename"), v8event->fileName);
+ stream.writeTextElement(QStringLiteral("line"), QString::number(v8event->line));
+ }
+ stream.writeTextElement(QStringLiteral("totalTime"), QString::number(v8event->totalTime));
+ stream.writeTextElement(QStringLiteral("selfTime"), QString::number(v8event->selfTime));
+ if (!v8event->v8children.isEmpty()) {
+ stream.writeStartElement(QStringLiteral("childrenEvents"));
+ QStringList childrenIndexes;
+ QStringList childrenTimes;
+ foreach (const QString &childHash, v8event->v8children.keys()) {
+ childrenIndexes << QString::number(v8EventIndex(childHash));
+ childrenTimes << QString::number(v8event->v8children[childHash]);
+ }
+
+ stream.writeAttribute(QStringLiteral("list"), childrenIndexes.join(QString(", ")));
+ stream.writeAttribute(QStringLiteral("childrenTimes"), childrenTimes.join(QString(", ")));
+ stream.writeEndElement();
+ }
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // v8 profiler output
+
+ stream.writeEndElement(); // trace
+ stream.writeEndDocument();
+
+ file.close();
+ return true;
+}
+
+int QmlProfilerData::v8EventIndex(const QString &hashStr)
+{
+ if (!d->v8EventHash.contains(hashStr)) {
+ emit error("Trying to index nonexisting v8 event");
+ return -1;
+ }
+ return d->v8EventHash.keys().indexOf( hashStr );
+}
+
+void QmlProfilerData::setState(QmlProfilerData::State state)
+{
+ // It's not an error, we are continuously calling "AcquiringData" for example
+ if (d->state == state)
+ return;
+
+ switch (state) {
+ case Empty:
+ // if it's not empty, complain but go on
+ if (!isEmpty())
+ emit error("Invalid qmlprofiler state change (Empty)");
+ break;
+ case AcquiringData:
+ // we're not supposed to receive new data while processing older data
+ if (d->state == ProcessingData)
+ emit error("Invalid qmlprofiler state change (AcquiringData)");
+ break;
+ case ProcessingData:
+ if (d->state != AcquiringData)
+ emit error("Invalid qmlprofiler state change (ProcessingData)");
+ break;
+ case Done:
+ if (d->state != ProcessingData && d->state != Empty)
+ emit error("Invalid qmlprofiler state change (Done)");
+ break;
+ default:
+ emit error("Trying to set unknown state in events list");
+ break;
+ }
+
+ d->state = state;
+ emit stateChanged();
+
+ // special: if we were done with an empty list, clean internal data and go back to empty
+ if (d->state == Done && isEmpty()) {
+ clear();
+ }
+ return;
+}
+
diff --git a/tools/qmlprofiler/qmlprofilerdata.h b/tools/qmlprofiler/qmlprofilerdata.h
new file mode 100644
index 0000000000..66209a1830
--- /dev/null
+++ b/tools/qmlprofiler/qmlprofilerdata.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERDATA_H
+#define QMLPROFILERDATA_H
+
+#include <QtQml/private/qqmlprofilerservice_p.h>
+#include "qmlprofilereventlocation.h"
+
+#include <QObject>
+
+class QmlProfilerDataPrivate;
+class QmlProfilerData : public QObject
+{
+ Q_OBJECT
+public:
+ enum State {
+ Empty,
+ AcquiringData,
+ ProcessingData,
+ Done
+ };
+
+ explicit QmlProfilerData(QObject *parent = 0);
+ ~QmlProfilerData();
+
+ static QString getHashStringForQmlEvent(const QmlEventLocation &location, int eventType);
+ static QString getHashStringForV8Event(const QString &displayName, const QString &function);
+ static QString qmlRangeTypeAsString(QQmlProfilerService::RangeType typeEnum);
+ static QString rootEventName();
+ static QString rootEventDescription();
+
+ qint64 traceStartTime() const;
+ qint64 traceEndTime() const;
+
+ bool isEmpty() const;
+
+signals:
+ void error(QString);
+ void stateChanged();
+ void dataReady();
+
+public slots:
+ void clear();
+ void setTraceEndTime(qint64 time);
+ void setTraceStartTime(qint64 time);
+ void addQmlEvent(QQmlProfilerService::RangeType type, qint64 startTime, qint64 duration,
+ const QStringList &data, const QmlEventLocation &location);
+ void addV8Event(int depth, const QString &function, const QString &filename,
+ int lineNumber, double totalTime, double selfTime);
+ void addFrameEvent(qint64 time, int framerate, int animationcount);
+
+ void complete();
+ bool save(const QString &filename);
+
+private:
+ void sortStartTimes();
+ int v8EventIndex(const QString &hashStr);
+ void computeQmlTime();
+ void setState(QmlProfilerData::State state);
+
+private:
+ QmlProfilerDataPrivate *d;
+};
+
+#endif // QMLPROFILERDATA_H
diff --git a/tools/qmlprofiler/qmlprofilereventlocation.h b/tools/qmlprofiler/qmlprofilereventlocation.h
new file mode 100644
index 0000000000..a34b6aba6d
--- /dev/null
+++ b/tools/qmlprofiler/qmlprofilereventlocation.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QMLPROFILEREVENTLOCATION_H
+#define QMLPROFILEREVENTLOCATION_H
+
+#include <QString>
+
+struct QmlEventLocation
+{
+ QmlEventLocation() : line(-1), column(-1) {}
+ QmlEventLocation(const QString &file, int lineNumber, int columnNumber)
+ : filename(file), line(lineNumber), column(columnNumber) {}
+ QString filename;
+ int line;
+ int column;
+};
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_TYPEINFO(QmlEventLocation, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+#endif // QMLPROFILEREVENTLOCATION_H
diff --git a/tools/tools.pro b/tools/tools.pro
index 34e90ed816..186c7e9c3b 100644
--- a/tools/tools.pro
+++ b/tools/tools.pro
@@ -1,5 +1,5 @@
TEMPLATE = subdirs
-SUBDIRS += qmlscene qmlplugindump qmlmin qmleasing qmlprofiler
+SUBDIRS += qmlscene qmlplugindump qmlmin qmleasing qmlprofiler easingcurveeditor
contains(QT_CONFIG, qmltest): SUBDIRS += qmltestrunner