summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKai Koehne <kai.koehne@digia.com>2013-06-05 16:55:32 +0200
committerKai Koehne <kai.koehne@digia.com>2013-06-06 16:01:42 +0200
commite96414023748a03f63a88886b163177a7a862946 (patch)
tree26c453cf3d48dc0345023376de1b45f67ca909d1
downloadqt-creator-e96414023748a03f63a88886b163177a7a862946.tar.gz
Initial commit of qmlprofiler plugin (commercial version)
Change-Id: I08e838110c17a4a2085e0c3232424787186f9c31
-rw-r--r--plugins/qmlprofiler/QmlProfiler.pluginspec.in17
-rw-r--r--plugins/qmlprofiler/abstractqmlprofilerrunner.h61
-rw-r--r--plugins/qmlprofiler/canvas/canvas.pri11
-rw-r--r--plugins/qmlprofiler/canvas/qdeclarativecanvas.cpp244
-rw-r--r--plugins/qmlprofiler/canvas/qdeclarativecanvas_p.h108
-rw-r--r--plugins/qmlprofiler/canvas/qdeclarativecanvastimer.cpp85
-rw-r--r--plugins/qmlprofiler/canvas/qdeclarativecanvastimer_p.h64
-rw-r--r--plugins/qmlprofiler/canvas/qdeclarativecontext2d.cpp1130
-rw-r--r--plugins/qmlprofiler/canvas/qdeclarativecontext2d_p.h324
-rw-r--r--plugins/qmlprofiler/canvas/qmlprofilercanvas.cpp91
-rw-r--r--plugins/qmlprofiler/canvas/qmlprofilercanvas.h82
-rw-r--r--plugins/qmlprofiler/localqmlprofilerrunner.cpp86
-rw-r--r--plugins/qmlprofiler/localqmlprofilerrunner.h74
-rw-r--r--plugins/qmlprofiler/qml/Detail.qml58
-rw-r--r--plugins/qmlprofiler/qml/Label.qml199
-rw-r--r--plugins/qmlprofiler/qml/MainView.qml612
-rw-r--r--plugins/qmlprofiler/qml/Overview.js190
-rw-r--r--plugins/qmlprofiler/qml/Overview.qml126
-rw-r--r--plugins/qmlprofiler/qml/RangeDetails.qml222
-rw-r--r--plugins/qmlprofiler/qml/RangeMover.qml203
-rw-r--r--plugins/qmlprofiler/qml/SelectionRange.qml297
-rw-r--r--plugins/qmlprofiler/qml/SelectionRangeDetails.qml189
-rw-r--r--plugins/qmlprofiler/qml/TimeDisplay.qml140
-rw-r--r--plugins/qmlprofiler/qml/TimeMarks.qml146
-rw-r--r--plugins/qmlprofiler/qml/arrow_down.pngbin0 -> 250 bytes
-rw-r--r--plugins/qmlprofiler/qml/arrow_right.pngbin0 -> 246 bytes
-rw-r--r--plugins/qmlprofiler/qml/clean_pane_small.pngbin0 -> 677 bytes
-rw-r--r--plugins/qmlprofiler/qml/dialog_shadow.pngbin0 -> 664 bytes
-rw-r--r--plugins/qmlprofiler/qml/ico_next.pngbin0 -> 715 bytes
-rw-r--r--plugins/qmlprofiler/qml/ico_prev.pngbin0 -> 745 bytes
-rw-r--r--plugins/qmlprofiler/qml/ico_rangeselected.pngbin0 -> 233 bytes
-rw-r--r--plugins/qmlprofiler/qml/ico_rangeselection.pngbin0 -> 224 bytes
-rw-r--r--plugins/qmlprofiler/qml/ico_selectionmode.pngbin0 -> 328 bytes
-rw-r--r--plugins/qmlprofiler/qml/ico_zoom.pngbin0 -> 584 bytes
-rw-r--r--plugins/qmlprofiler/qml/lock_closed.pngbin0 -> 276 bytes
-rw-r--r--plugins/qmlprofiler/qml/lock_open.pngbin0 -> 265 bytes
-rw-r--r--plugins/qmlprofiler/qml/qmlprofiler.qrc30
-rw-r--r--plugins/qmlprofiler/qml/range_handle.pngbin0 -> 197 bytes
-rw-r--r--plugins/qmlprofiler/qml/recordOff.pngbin0 -> 496 bytes
-rw-r--r--plugins/qmlprofiler/qml/recordOn.pngbin0 -> 614 bytes
-rw-r--r--plugins/qmlprofiler/qmlprofiler.pro74
-rw-r--r--plugins/qmlprofiler/qmlprofiler.qbs96
-rw-r--r--plugins/qmlprofiler/qmlprofiler_dependencies.pri13
-rw-r--r--plugins/qmlprofiler/qmlprofiler_global.h42
-rw-r--r--plugins/qmlprofiler/qmlprofilerattachdialog.cpp109
-rw-r--r--plugins/qmlprofiler/qmlprofilerattachdialog.h63
-rw-r--r--plugins/qmlprofiler/qmlprofilerattachdialog.ui129
-rw-r--r--plugins/qmlprofiler/qmlprofilerclientmanager.cpp431
-rw-r--r--plugins/qmlprofiler/qmlprofilerclientmanager.h102
-rw-r--r--plugins/qmlprofiler/qmlprofilerconstants.h42
-rw-r--r--plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp211
-rw-r--r--plugins/qmlprofiler/qmlprofilerdetailsrewriter.h70
-rw-r--r--plugins/qmlprofiler/qmlprofilerengine.cpp388
-rw-r--r--plugins/qmlprofiler/qmlprofilerengine.h84
-rw-r--r--plugins/qmlprofiler/qmlprofilereventsmodelproxy.cpp467
-rw-r--r--plugins/qmlprofiler/qmlprofilereventsmodelproxy.h172
-rw-r--r--plugins/qmlprofiler/qmlprofilereventview.cpp922
-rw-r--r--plugins/qmlprofiler/qmlprofilereventview.h202
-rw-r--r--plugins/qmlprofiler/qmlprofilermodelmanager.cpp309
-rw-r--r--plugins/qmlprofiler/qmlprofilermodelmanager.h147
-rw-r--r--plugins/qmlprofiler/qmlprofileroverviewmodelproxy.cpp227
-rw-r--r--plugins/qmlprofiler/qmlprofileroverviewmodelproxy.h105
-rw-r--r--plugins/qmlprofiler/qmlprofilerplugin.cpp72
-rw-r--r--plugins/qmlprofiler/qmlprofilerplugin.h59
-rw-r--r--plugins/qmlprofiler/qmlprofilerprocessedmodel.cpp181
-rw-r--r--plugins/qmlprofiler/qmlprofilerprocessedmodel.h62
-rw-r--r--plugins/qmlprofiler/qmlprofilersimplemodel.cpp101
-rw-r--r--plugins/qmlprofiler/qmlprofilersimplemodel.h81
-rw-r--r--plugins/qmlprofiler/qmlprofilerstatemanager.cpp177
-rw-r--r--plugins/qmlprofiler/qmlprofilerstatemanager.h80
-rw-r--r--plugins/qmlprofiler/qmlprofilerstatewidget.cpp282
-rw-r--r--plugins/qmlprofiler/qmlprofilerstatewidget.h66
-rw-r--r--plugins/qmlprofiler/qmlprofilertimelinemodelproxy.cpp624
-rw-r--r--plugins/qmlprofiler/qmlprofilertimelinemodelproxy.h165
-rw-r--r--plugins/qmlprofiler/qmlprofilertool.cpp734
-rw-r--r--plugins/qmlprofiler/qmlprofilertool.h113
-rw-r--r--plugins/qmlprofiler/qmlprofilertracefile.cpp485
-rw-r--r--plugins/qmlprofiler/qmlprofilertracefile.h127
-rw-r--r--plugins/qmlprofiler/qmlprofilertraceview.cpp604
-rw-r--r--plugins/qmlprofiler/qmlprofilertraceview.h157
-rw-r--r--plugins/qmlprofiler/qmlprofilertreeview.cpp46
-rw-r--r--plugins/qmlprofiler/qmlprofilertreeview.h49
-rw-r--r--plugins/qmlprofiler/qmlprofilerv8eventsview.cpp1016
-rw-r--r--plugins/qmlprofiler/qmlprofilerv8eventsview.h202
-rw-r--r--plugins/qmlprofiler/qmlprofilerviewmanager.cpp167
-rw-r--r--plugins/qmlprofiler/qmlprofilerviewmanager.h76
-rw-r--r--plugins/qmlprofiler/qv8profilerdatamodel.cpp495
-rw-r--r--plugins/qmlprofiler/qv8profilerdatamodel.h112
-rw-r--r--plugins/qmlprofiler/qv8profilereventview.cpp742
-rw-r--r--plugins/qmlprofiler/qv8profilereventview.h181
-rw-r--r--plugins/qmlprofiler/timelinerenderer.cpp489
-rw-r--r--plugins/qmlprofiler/timelinerenderer.h227
-rw-r--r--qmlprofiler.pro3
-rw-r--r--qtcreatorplugin.pri7
94 files changed, 17176 insertions, 0 deletions
diff --git a/plugins/qmlprofiler/QmlProfiler.pluginspec.in b/plugins/qmlprofiler/QmlProfiler.pluginspec.in
new file mode 100644
index 0000000000..65dc165b7f
--- /dev/null
+++ b/plugins/qmlprofiler/QmlProfiler.pluginspec.in
@@ -0,0 +1,17 @@
+<plugin name=\"QmlProfiler\" version=\"$$QTCREATOR_VERSION\" compatVersion=\"$$QTCREATOR_VERSION\">
+ <vendor>Digia Plc</vendor>
+ <copyright>(C) 2013 Digia Plc</copyright>
+ <license>
+ Commercial Usage
+
+ Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Digia.
+
+ GNU Lesser General Public License Usage
+
+ Alternatively, this plugin may be used under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. 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.
+ </license>
+ <category>Qt Quick</category>
+ <description>Qml Profiler Plugin</description>
+ <url>http://www.qt-project.org</url>
+ $$dependencyList
+</plugin>
diff --git a/plugins/qmlprofiler/abstractqmlprofilerrunner.h b/plugins/qmlprofiler/abstractqmlprofilerrunner.h
new file mode 100644
index 0000000000..233941ed96
--- /dev/null
+++ b/plugins/qmlprofiler/abstractqmlprofilerrunner.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef ABSTRACTQMLPROFILERRUNNER_H
+#define ABSTRACTQMLPROFILERRUNNER_H
+
+#include <QObject>
+#include <utils/outputformat.h>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class AbstractQmlProfilerRunner : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit AbstractQmlProfilerRunner(QObject *parent = 0) : QObject(parent) { }
+
+ virtual void start() = 0;
+ virtual void stop() = 0;
+
+ virtual quint16 debugPort() const = 0;
+
+signals:
+ void started();
+ void stopped();
+
+ void appendMessage(const QString &message, Utils::OutputFormat format);
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // ABSTRACTQMLPROFILERRUNNER_H
diff --git a/plugins/qmlprofiler/canvas/canvas.pri b/plugins/qmlprofiler/canvas/canvas.pri
new file mode 100644
index 0000000000..8b5621fb44
--- /dev/null
+++ b/plugins/qmlprofiler/canvas/canvas.pri
@@ -0,0 +1,11 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += $$PWD/qdeclarativecontext2d_p.h \
+ $$PWD/qdeclarativecanvas_p.h \
+ $$PWD/qmlprofilercanvas.h \
+ $$PWD/qdeclarativecanvastimer_p.h
+
+SOURCES += $$PWD/qdeclarativecontext2d.cpp \
+ $$PWD/qdeclarativecanvas.cpp \
+ $$PWD/qmlprofilercanvas.cpp \
+ $$PWD/qdeclarativecanvastimer.cpp
diff --git a/plugins/qmlprofiler/canvas/qdeclarativecanvas.cpp b/plugins/qmlprofiler/canvas/qdeclarativecanvas.cpp
new file mode 100644
index 0000000000..70a849f39b
--- /dev/null
+++ b/plugins/qmlprofiler/canvas/qdeclarativecanvas.cpp
@@ -0,0 +1,244 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qdeclarativecanvas_p.h"
+#include "qdeclarativecanvastimer_p.h"
+#include "qdeclarativecontext2d_p.h"
+
+#include <qpainter.h>
+
+QT_BEGIN_NAMESPACE
+
+Canvas::Canvas(QDeclarativeItem *parent)
+ : QDeclarativeItem(parent),
+ m_context(new Context2D(this)),
+ m_canvasWidth(0),
+ m_canvasHeight(0),
+ m_fillMode(Canvas::Stretch),
+ m_color(Qt::white)
+{
+ setFlag(QGraphicsItem::ItemHasNoContents, false);
+ setCacheMode(QGraphicsItem::DeviceCoordinateCache);
+}
+
+
+void Canvas::componentComplete()
+{
+ if (m_canvasWidth == 0 && m_canvasHeight == 0)
+ m_context->setSize(width(), height());
+ else
+ m_context->setSize(m_canvasWidth, m_canvasHeight);
+
+ connect(m_context, SIGNAL(changed()), this, SLOT(requestPaint()));
+ emit init();
+ QDeclarativeItem::componentComplete();
+}
+
+void Canvas::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
+{
+ m_context->setInPaint(true);
+ emit paint();
+
+ bool oldAA = painter->testRenderHint(QPainter::Antialiasing);
+ bool oldSmooth = painter->testRenderHint(QPainter::SmoothPixmapTransform);
+ if (smooth())
+ painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, smooth());
+
+ if (m_context->pixmap().isNull()) {
+ painter->fillRect(0, 0, width(), height(), m_color);
+ } else if (width() != m_context->pixmap().width() || height() != m_context->pixmap().height()) {
+ if (m_fillMode>= Tile) {
+ if (m_fillMode== Tile) {
+ painter->drawTiledPixmap(QRectF(0,0,width(),height()), m_context->pixmap());
+ } else {
+ qreal widthScale = width() / qreal(m_context->pixmap().width());
+ qreal heightScale = height() / qreal(m_context->pixmap().height());
+
+ QTransform scale;
+ if (m_fillMode== TileVertically) {
+ scale.scale(widthScale, 1.0);
+ QTransform old = painter->transform();
+ painter->setWorldTransform(scale * old);
+ painter->drawTiledPixmap(QRectF(0,0,m_context->pixmap().width(),height()), m_context->pixmap());
+ painter->setWorldTransform(old);
+ } else {
+ scale.scale(1.0, heightScale);
+ QTransform old = painter->transform();
+ painter->setWorldTransform(scale * old);
+ painter->drawTiledPixmap(QRectF(0,0,width(),m_context->pixmap().height()), m_context->pixmap());
+ painter->setWorldTransform(old);
+ }
+ }
+ } else {
+ qreal widthScale = width() / qreal(m_context->pixmap().width());
+ qreal heightScale = height() / qreal(m_context->pixmap().height());
+
+ QTransform scale;
+
+ if (m_fillMode== PreserveAspectFit) {
+ if (widthScale <= heightScale) {
+ heightScale = widthScale;
+ scale.translate(0, (height() - heightScale * m_context->pixmap().height()) / 2);
+ } else if (heightScale < widthScale) {
+ widthScale = heightScale;
+ scale.translate((width() - widthScale * m_context->pixmap().width()) / 2, 0);
+ }
+ } else if (m_fillMode== PreserveAspectCrop) {
+ if (widthScale < heightScale) {
+ widthScale = heightScale;
+ scale.translate((width() - widthScale * m_context->pixmap().width()) / 2, 0);
+ } else if (heightScale < widthScale) {
+ heightScale = widthScale;
+ scale.translate(0, (height() - heightScale * m_context->pixmap().height()) / 2);
+ }
+ }
+ if (clip()) {
+ painter->save();
+ painter->setClipRect(boundingRect(), Qt::IntersectClip);
+ }
+ scale.scale(widthScale, heightScale);
+ QTransform old = painter->transform();
+ painter->setWorldTransform(scale * old);
+ painter->drawPixmap(0, 0, m_context->pixmap());
+ painter->setWorldTransform(old);
+ if (clip())
+ painter->restore();
+ }
+ } else {
+ painter->drawPixmap(0, 0, m_context->pixmap());
+ }
+
+ if (smooth()) {
+ painter->setRenderHint(QPainter::Antialiasing, oldAA);
+ painter->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth);
+ }
+ m_context->setInPaint(false);
+}
+
+Context2D *Canvas::getContext(const QString &contextId)
+{
+ if (contextId == QLatin1String("2d"))
+ return m_context;
+ qDebug("Canvas:requesting unsupported context");
+ return 0;
+}
+
+void Canvas::requestPaint()
+{
+ update();
+}
+
+void Canvas::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ if (m_canvasWidth == 0 && m_canvasHeight == 0
+ && newGeometry.width() > 0 && newGeometry.height() > 0) {
+ m_context->setSize(width(), height());
+ }
+ QDeclarativeItem::geometryChanged(newGeometry, oldGeometry);
+}
+
+void Canvas::setCanvasWidth(int newWidth)
+{
+ if (m_canvasWidth != newWidth) {
+ m_canvasWidth = newWidth;
+ m_context->setSize(m_canvasWidth, m_canvasHeight);
+ emit canvasWidthChanged();
+ }
+}
+
+void Canvas::setCanvasHeight(int newHeight)
+{
+ if (m_canvasHeight != newHeight) {
+ m_canvasHeight = newHeight;
+ m_context->setSize(m_canvasWidth, m_canvasHeight);
+ emit canvasHeightChanged();
+ }
+}
+
+void Canvas::setFillMode(FillMode mode)
+{
+ if (m_fillMode == mode)
+ return;
+
+ m_fillMode = mode;
+ update();
+ emit fillModeChanged();
+}
+
+QColor Canvas::color()
+{
+ return m_color;
+}
+
+void Canvas::setColor(const QColor &color)
+{
+ if (m_color !=color) {
+ m_color = color;
+ colorChanged();
+ }
+}
+
+Canvas::FillMode Canvas::fillMode() const
+{
+ return m_fillMode;
+}
+
+bool Canvas::save(const QString &filename) const
+{
+ return m_context->pixmap().save(filename);
+}
+
+CanvasImage *Canvas::toImage() const
+{
+ return new CanvasImage(m_context->pixmap());
+}
+
+void Canvas::setTimeout(const QScriptValue &handler, long timeout)
+{
+ if (handler.isFunction())
+ CanvasTimer::createTimer(this, handler, timeout, true);
+}
+
+void Canvas::setInterval(const QScriptValue &handler, long interval)
+{
+ if (handler.isFunction())
+ CanvasTimer::createTimer(this, handler, interval, false);
+}
+
+void Canvas::clearTimeout(const QScriptValue &handler)
+{
+ CanvasTimer::removeTimer(handler);
+}
+
+void Canvas::clearInterval(const QScriptValue &handler)
+{
+ CanvasTimer::removeTimer(handler);
+}
+
+QT_END_NAMESPACE
diff --git a/plugins/qmlprofiler/canvas/qdeclarativecanvas_p.h b/plugins/qmlprofiler/canvas/qdeclarativecanvas_p.h
new file mode 100644
index 0000000000..429097d5b4
--- /dev/null
+++ b/plugins/qmlprofiler/canvas/qdeclarativecanvas_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVECANVAS_P_H
+#define QDECLARATIVECANVAS_P_H
+
+#include <qdeclarativeitem.h>
+
+#include "qdeclarativecontext2d_p.h"
+#include "qdeclarativecanvastimer_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+class Canvas : public QDeclarativeItem
+{
+ Q_OBJECT
+
+ Q_ENUMS(FillMode)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged);
+ Q_PROPERTY(int canvasWidth READ canvasWidth WRITE setCanvasWidth NOTIFY canvasWidthChanged);
+ Q_PROPERTY(int canvasHeight READ canvasHeight WRITE setCanvasHeight NOTIFY canvasHeightChanged);
+ Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged)
+
+public:
+ Canvas(QDeclarativeItem *parent = 0);
+ enum FillMode { Stretch, PreserveAspectFit, PreserveAspectCrop, Tile, TileVertically, TileHorizontally };
+
+
+ void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
+ void setCanvasWidth(int newWidth);
+ int canvasWidth() {return m_canvasWidth;}
+
+ void setCanvasHeight(int canvasHeight);
+ int canvasHeight() {return m_canvasHeight;}
+
+ void componentComplete();
+
+
+public Q_SLOTS:
+ Context2D *getContext(const QString & = QLatin1String("2d"));
+ void requestPaint();
+
+ FillMode fillMode() const;
+ void setFillMode(FillMode);
+
+ QColor color();
+ void setColor(const QColor &);
+
+ // Save current canvas to disk
+ bool save(const QString& filename) const;
+
+ // Timers
+ void setInterval(const QScriptValue &handler, long timeout);
+ void setTimeout(const QScriptValue &handler, long timeout);
+ void clearInterval(const QScriptValue &handler);
+ void clearTimeout(const QScriptValue &handler);
+
+Q_SIGNALS:
+ void fillModeChanged();
+ void canvasWidthChanged();
+ void canvasHeightChanged();
+ void colorChanged();
+ void init();
+ void paint();
+
+private:
+ // Return canvas contents as a drawable image
+ CanvasImage *toImage() const;
+ Context2D *m_context;
+ int m_canvasWidth;
+ int m_canvasHeight;
+ FillMode m_fillMode;
+ QColor m_color;
+
+ friend class Context2D;
+};
+
+QT_END_NAMESPACE
+
+#endif //QDECLARATIVECANVAS_P_H
diff --git a/plugins/qmlprofiler/canvas/qdeclarativecanvastimer.cpp b/plugins/qmlprofiler/canvas/qdeclarativecanvastimer.cpp
new file mode 100644
index 0000000000..4d41aec983
--- /dev/null
+++ b/plugins/qmlprofiler/canvas/qdeclarativecanvastimer.cpp
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qdeclarativecanvastimer_p.h"
+
+#include <qscriptengine.h>
+#include <qscriptvalue.h>
+#include <qtimer.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_GLOBAL_STATIC(QList<CanvasTimer*> , activeTimers);
+
+CanvasTimer::CanvasTimer(QObject *parent, const QScriptValue &data)
+ : QTimer(parent), m_value(data)
+{
+}
+
+void CanvasTimer::handleTimeout()
+{
+ Q_ASSERT(m_value.isFunction());
+ m_value.call();
+ if (isSingleShot())
+ removeTimer(this);
+}
+
+void CanvasTimer::createTimer(QObject *parent, const QScriptValue &val, long timeout, bool singleshot)
+{
+
+ CanvasTimer *timer = new CanvasTimer(parent, val);
+ timer->setInterval(timeout);
+ timer->setSingleShot(singleshot);
+ connect(timer, SIGNAL(timeout()), timer, SLOT(handleTimeout()));
+ activeTimers()->append(timer);
+ timer->start();
+}
+
+void CanvasTimer::removeTimer(CanvasTimer *timer)
+{
+ activeTimers()->removeAll(timer);
+ timer->deleteLater();
+}
+
+void CanvasTimer::removeTimer(const QScriptValue &val)
+{
+ if (!val.isFunction())
+ return;
+
+ for (int i = 0 ; i < activeTimers()->count() ; ++i) {
+ CanvasTimer *timer = activeTimers()->at(i);
+ if (timer->equals(val)) {
+ removeTimer(timer);
+ return;
+ }
+ }
+}
+
+QT_END_NAMESPACE
+
diff --git a/plugins/qmlprofiler/canvas/qdeclarativecanvastimer_p.h b/plugins/qmlprofiler/canvas/qdeclarativecanvastimer_p.h
new file mode 100644
index 0000000000..915afa542d
--- /dev/null
+++ b/plugins/qmlprofiler/canvas/qdeclarativecanvastimer_p.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVECANVASTIMER_P_H
+#define QDECLARATIVECANVASTIMER_P_H
+
+#include <qscriptvalue.h>
+#include <qtimer.h>
+#include <qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class CanvasTimer : public QTimer
+{
+ Q_OBJECT
+
+public:
+ CanvasTimer(QObject *parent, const QScriptValue &data);
+
+public Q_SLOTS:
+ void handleTimeout();
+ bool equals(const QScriptValue &value){return m_value.equals(value);}
+
+public:
+ static void createTimer(QObject *parent, const QScriptValue &val, long timeout, bool singleshot);
+ static void removeTimer(CanvasTimer *timer);
+ static void removeTimer(const QScriptValue &);
+
+private:
+ QScriptValue m_value;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QDECLARATIVECANVASTIMER_P_H
diff --git a/plugins/qmlprofiler/canvas/qdeclarativecontext2d.cpp b/plugins/qmlprofiler/canvas/qdeclarativecontext2d.cpp
new file mode 100644
index 0000000000..62e7f09207
--- /dev/null
+++ b/plugins/qmlprofiler/canvas/qdeclarativecontext2d.cpp
@@ -0,0 +1,1130 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qdeclarativecontext2d_p.h"
+
+#include "qdeclarativecanvas_p.h"
+
+#include <qdebug.h>
+#include <math.h>
+
+#include <qgraphicsitem.h>
+#include <qapplication.h>
+#include <qgraphicseffect.h>
+
+#include <QImage>
+
+QT_BEGIN_NAMESPACE
+
+static const double Q_PI = 3.14159265358979323846; // pi
+
+class CustomDropShadowEffect : public QGraphicsDropShadowEffect
+{
+public:
+ void draw(QPainter *painter) { QGraphicsDropShadowEffect::draw(painter);}
+ void drawSource(QPainter *painter) { QGraphicsDropShadowEffect::drawSource(painter);}
+};
+
+// Note, this is exported but in a private header as qtopengl depends on it.
+// But it really should be considered private API
+void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0);
+void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0);
+
+#define DEGREES(t) ((t) * 180.0 / Q_PI)
+
+#define qClamp(val, min, max) qMin(qMax(val, min), max)
+static QList<qreal> parseNumbersList(QString::const_iterator &itr)
+{
+ QList<qreal> points;
+ QString temp;
+ while ((*itr).isSpace())
+ ++itr;
+ while ((*itr).isNumber() ||
+ (*itr) == QLatin1Char('-') || (*itr) == QLatin1Char('+') || (*itr) == QLatin1Char('.')) {
+ temp.clear();
+
+ if ((*itr) == QLatin1Char('-'))
+ temp += *itr++;
+ else if ((*itr) == QLatin1Char('+'))
+ temp += *itr++;
+ while ((*itr).isDigit())
+ temp += *itr++;
+ if ((*itr) == QLatin1Char('.'))
+ temp += *itr++;
+ while ((*itr).isDigit())
+ temp += *itr++;
+ while ((*itr).isSpace())
+ ++itr;
+ if ((*itr) == QLatin1Char(','))
+ ++itr;
+ points.append(temp.toDouble());
+ //eat spaces
+ while ((*itr).isSpace())
+ ++itr;
+ }
+
+ return points;
+}
+
+QColor colorFromString(const QString &name)
+{
+ QString::const_iterator itr = name.constBegin();
+ QList<qreal> compo;
+ if (name.startsWith(QLatin1String("rgba("))) {
+ ++itr; ++itr; ++itr; ++itr; ++itr;
+ compo = parseNumbersList(itr);
+ if (compo.size() != 4)
+ return QColor();
+ //alpha seems to be always between 0-1
+ compo[3] *= 255;
+ return QColor((int)compo[0], (int)compo[1],
+ (int)compo[2], (int)compo[3]);
+ } else if (name.startsWith(QLatin1String("rgb("))) {
+ ++itr; ++itr; ++itr; ++itr;
+ compo = parseNumbersList(itr);
+ if (compo.size() != 3)
+ return QColor();
+ return QColor((int)qClamp(compo[0], qreal(0), qreal(255)),
+ (int)qClamp(compo[1], qreal(0), qreal(255)),
+ (int)qClamp(compo[2], qreal(0), qreal(255)));
+ } else if (name.startsWith(QLatin1String("hsla("))) {
+ ++itr; ++itr; ++itr; ++itr; ++itr;
+ compo = parseNumbersList(itr);
+ if (compo.size() != 4)
+ return QColor();
+ return QColor::fromHslF(compo[0], compo[1],
+ compo[2], compo[3]);
+ } else if (name.startsWith(QLatin1String("hsl("))) {
+ ++itr; ++itr; ++itr; ++itr; ++itr;
+ compo = parseNumbersList(itr);
+ if (compo.size() != 3)
+ return QColor();
+ return QColor::fromHslF(compo[0], compo[1],
+ compo[2]);
+ } else {
+ //QRgb color;
+ //CSSParser::parseColor(name, color);
+ return QColor(name);
+ }
+}
+
+
+static QPainter::CompositionMode compositeOperatorFromString(const QString &compositeOperator)
+{
+ if (compositeOperator == QLatin1String("source-over"))
+ return QPainter::CompositionMode_SourceOver;
+ else if (compositeOperator == QLatin1String("source-out"))
+ return QPainter::CompositionMode_SourceOut;
+ else if (compositeOperator == QLatin1String("source-in"))
+ return QPainter::CompositionMode_SourceIn;
+ else if (compositeOperator == QLatin1String("source-atop"))
+ return QPainter::CompositionMode_SourceAtop;
+ else if (compositeOperator == QLatin1String("destination-atop"))
+ return QPainter::CompositionMode_DestinationAtop;
+ else if (compositeOperator == QLatin1String("destination-in"))
+ return QPainter::CompositionMode_DestinationIn;
+ else if (compositeOperator == QLatin1String("destination-out"))
+ return QPainter::CompositionMode_DestinationOut;
+ else if (compositeOperator == QLatin1String("destination-over"))
+ return QPainter::CompositionMode_DestinationOver;
+ else if (compositeOperator == QLatin1String("darker"))
+ return QPainter::CompositionMode_SourceOver;
+ else if (compositeOperator == QLatin1String("lighter"))
+ return QPainter::CompositionMode_SourceOver;
+ else if (compositeOperator == QLatin1String("copy"))
+ return QPainter::CompositionMode_Source;
+ else if (compositeOperator == QLatin1String("xor"))
+ return QPainter::CompositionMode_Xor;
+
+ return QPainter::CompositionMode_SourceOver;
+}
+
+static QString compositeOperatorToString(QPainter::CompositionMode op)
+{
+ switch (op) {
+ case QPainter::CompositionMode_SourceOver:
+ return QLatin1String("source-over");
+ case QPainter::CompositionMode_DestinationOver:
+ return QLatin1String("destination-over");
+ case QPainter::CompositionMode_Clear:
+ return QLatin1String("clear");
+ case QPainter::CompositionMode_Source:
+ return QLatin1String("source");
+ case QPainter::CompositionMode_Destination:
+ return QLatin1String("destination");
+ case QPainter::CompositionMode_SourceIn:
+ return QLatin1String("source-in");
+ case QPainter::CompositionMode_DestinationIn:
+ return QLatin1String("destination-in");
+ case QPainter::CompositionMode_SourceOut:
+ return QLatin1String("source-out");
+ case QPainter::CompositionMode_DestinationOut:
+ return QLatin1String("destination-out");
+ case QPainter::CompositionMode_SourceAtop:
+ return QLatin1String("source-atop");
+ case QPainter::CompositionMode_DestinationAtop:
+ return QLatin1String("destination-atop");
+ case QPainter::CompositionMode_Xor:
+ return QLatin1String("xor");
+ case QPainter::CompositionMode_Plus:
+ return QLatin1String("plus");
+ case QPainter::CompositionMode_Multiply:
+ return QLatin1String("multiply");
+ case QPainter::CompositionMode_Screen:
+ return QLatin1String("screen");
+ case QPainter::CompositionMode_Overlay:
+ return QLatin1String("overlay");
+ case QPainter::CompositionMode_Darken:
+ return QLatin1String("darken");
+ case QPainter::CompositionMode_Lighten:
+ return QLatin1String("lighten");
+ case QPainter::CompositionMode_ColorDodge:
+ return QLatin1String("color-dodge");
+ case QPainter::CompositionMode_ColorBurn:
+ return QLatin1String("color-burn");
+ case QPainter::CompositionMode_HardLight:
+ return QLatin1String("hard-light");
+ case QPainter::CompositionMode_SoftLight:
+ return QLatin1String("soft-light");
+ case QPainter::CompositionMode_Difference:
+ return QLatin1String("difference");
+ case QPainter::CompositionMode_Exclusion:
+ return QLatin1String("exclusion");
+ default:
+ break;
+ }
+ return QString();
+}
+
+void Context2D::save()
+{
+ m_stateStack.push(m_state);
+}
+
+
+void Context2D::restore()
+{
+ if (!m_stateStack.isEmpty()) {
+ m_state = m_stateStack.pop();
+ m_state.flags = AllIsFullOfDirt;
+ }
+}
+
+
+void Context2D::scale(qreal x, qreal y)
+{
+ m_state.matrix.scale(x, y);
+ m_state.flags |= DirtyTransformationMatrix;
+}
+
+
+void Context2D::rotate(qreal angle)
+{
+ m_state.matrix.rotate(DEGREES(angle));
+ m_state.flags |= DirtyTransformationMatrix;
+}
+
+
+void Context2D::translate(qreal x, qreal y)
+{
+ m_state.matrix.translate(x, y);
+ m_state.flags |= DirtyTransformationMatrix;
+}
+
+
+void Context2D::transform(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy)
+{
+ QMatrix mat(m11, m12,
+ m21, m22,
+ dx, dy);
+ m_state.matrix *= mat;
+ m_state.flags |= DirtyTransformationMatrix;
+}
+
+
+void Context2D::setTransform(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy)
+{
+ QMatrix mat(m11, m12,
+ m21, m22,
+ dx, dy);
+ m_state.matrix = mat;
+ m_state.flags |= DirtyTransformationMatrix;
+}
+
+
+QString Context2D::globalCompositeOperation() const
+{
+ return compositeOperatorToString(m_state.globalCompositeOperation);
+}
+
+void Context2D::setGlobalCompositeOperation(const QString &op)
+{
+ QPainter::CompositionMode mode =
+ compositeOperatorFromString(op);
+ m_state.globalCompositeOperation = mode;
+ m_state.flags |= DirtyGlobalCompositeOperation;
+}
+
+QVariant Context2D::strokeStyle() const
+{
+ return m_state.strokeStyle;
+}
+
+void Context2D::setStrokeStyle(const QVariant &style)
+{
+ CanvasGradient * gradient= qobject_cast<CanvasGradient*>(style.value<QObject*>());
+ if (gradient) {
+ m_state.strokeStyle = gradient->value();
+ } else {
+ QColor color = colorFromString(style.toString());
+ m_state.strokeStyle = color;
+ }
+ m_state.flags |= DirtyStrokeStyle;
+}
+
+QVariant Context2D::fillStyle() const
+{
+ return m_state.fillStyle;
+}
+
+void Context2D::setFillStyle(const QVariant &style)
+{
+ CanvasGradient * gradient= qobject_cast<CanvasGradient*>(style.value<QObject*>());
+ if (gradient) {
+ m_state.fillStyle = gradient->value();
+ } else {
+ QColor color = colorFromString(style.toString());
+ m_state.fillStyle = color;
+ }
+ m_state.flags |= DirtyFillStyle;
+}
+
+qreal Context2D::globalAlpha() const
+{
+ return m_state.globalAlpha;
+}
+
+void Context2D::setGlobalAlpha(qreal alpha)
+{
+ m_state.globalAlpha = alpha;
+ m_state.flags |= DirtyGlobalAlpha;
+}
+
+CanvasImage *Context2D::createImage(const QString &url)
+{
+ return new CanvasImage(url);
+}
+
+CanvasGradient *Context2D::createLinearGradient(qreal x0, qreal y0,
+ qreal x1, qreal y1)
+{
+ QLinearGradient g(x0, y0, x1, y1);
+ return new CanvasGradient(g);
+}
+
+
+CanvasGradient *Context2D::createRadialGradient(qreal x0, qreal y0,
+ qreal r0, qreal x1,
+ qreal y1, qreal r1)
+{
+ QRadialGradient g(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
+ return new CanvasGradient(g);
+}
+
+qreal Context2D::lineWidth() const
+{
+ return m_state.lineWidth;
+}
+
+void Context2D::setLineWidth(qreal w)
+{
+ m_state.lineWidth = w;
+ m_state.flags |= DirtyLineWidth;
+}
+
+QString Context2D::lineCap() const
+{
+ switch (m_state.lineCap) {
+ case Qt::FlatCap:
+ return QLatin1String("butt");
+ case Qt::SquareCap:
+ return QLatin1String("square");
+ case Qt::RoundCap:
+ return QLatin1String("round");
+ default: ;
+ }
+ return QString();
+}
+
+void Context2D::setLineCap(const QString &capString)
+{
+ Qt::PenCapStyle style;
+ if (capString == QLatin1String("round"))
+ style = Qt::RoundCap;
+ else if (capString == QLatin1String("square"))
+ style = Qt::SquareCap;
+ else //if (capString == QLatin1String("butt"))
+ style = Qt::FlatCap;
+ m_state.lineCap = style;
+ m_state.flags |= DirtyLineCap;
+}
+
+QString Context2D::lineJoin() const
+{
+ switch (m_state.lineJoin) {
+ case Qt::RoundJoin:
+ return QLatin1String("round");
+ case Qt::BevelJoin:
+ return QLatin1String("bevel");
+ case Qt::MiterJoin:
+ return QLatin1String("miter");
+ default: ;
+ }
+ return QString();
+}
+
+void Context2D::setLineJoin(const QString &joinString)
+{
+ Qt::PenJoinStyle style;
+ if (joinString == QLatin1String("round"))
+ style = Qt::RoundJoin;
+ else if (joinString == QLatin1String("bevel"))
+ style = Qt::BevelJoin;
+ else //if (joinString == "miter")
+ style = Qt::MiterJoin;
+ m_state.lineJoin = style;
+ m_state.flags |= DirtyLineJoin;
+}
+
+qreal Context2D::miterLimit() const
+{
+ return m_state.miterLimit;
+}
+
+void Context2D::setMiterLimit(qreal m)
+{
+ m_state.miterLimit = m;
+ m_state.flags |= DirtyMiterLimit;
+}
+
+void Context2D::setShadowOffsetX(qreal x)
+{
+ if (m_state.shadowOffsetX == x)
+ return;
+ m_state.shadowOffsetX = x;
+ updateShadowBuffer();
+ if (m_painter.device() == &m_shadowbuffer && m_state.shadowBlur>0)
+ endPainting();
+ m_state.flags |= DirtyShadowOffsetX;
+}
+
+const QList<Context2D::MouseArea> &Context2D::mouseAreas() const
+{
+ return m_mouseAreas;
+}
+
+void Context2D::updateShadowBuffer() {
+ if (m_shadowbuffer.isNull() || m_shadowbuffer.width() != m_width+m_state.shadowOffsetX ||
+ m_shadowbuffer.height() != m_height+m_state.shadowOffsetY) {
+ m_shadowbuffer = QImage(m_width+m_state.shadowOffsetX, m_height+m_state.shadowOffsetY, QImage::Format_ARGB32);
+ m_shadowbuffer.fill(Qt::transparent);
+ }
+}
+
+void Context2D::setShadowOffsetY(qreal y)
+{
+ if (m_state.shadowOffsetY == y)
+ return;
+ m_state.shadowOffsetY = y;
+ updateShadowBuffer();
+ if (m_painter.device() == &m_shadowbuffer && m_state.shadowBlur>0)
+ endPainting();
+
+ m_state.flags |= DirtyShadowOffsetY;
+}
+
+void Context2D::setShadowBlur(qreal b)
+{
+ if (m_state.shadowBlur == b)
+ return;
+ m_state.shadowBlur = b;
+ updateShadowBuffer();
+ if (m_painter.device() == &m_shadowbuffer && m_state.shadowBlur>0)
+ endPainting();
+ m_state.flags |= DirtyShadowBlur;
+}
+
+void Context2D::setShadowColor(const QString &str)
+{
+ m_state.shadowColor = colorFromString(str);
+ if (m_painter.device() == &m_shadowbuffer && m_state.shadowBlur>0)
+ endPainting();
+ m_state.flags |= DirtyShadowColor;
+}
+
+QString Context2D::textBaseline()
+{
+ switch (m_state.textBaseline) {
+ case Context2D::Alphabetic:
+ return QLatin1String("alphabetic");
+ case Context2D::Hanging:
+ return QLatin1String("hanging");
+ case Context2D::Bottom:
+ return QLatin1String("bottom");
+ case Context2D::Top:
+ return QLatin1String("top");
+ case Context2D::Middle:
+ return QLatin1String("middle");
+ default:
+ Q_ASSERT("invalid value");
+ return QLatin1String("start");
+ }
+}
+
+void Context2D::setTextBaseline(const QString &baseline)
+{
+ if (baseline==QLatin1String("alphabetic"))
+ m_state.textBaseline = Context2D::Alphabetic;
+ else if (baseline == QLatin1String("hanging"))
+ m_state.textBaseline = Context2D::Hanging;
+ else if (baseline == QLatin1String("top"))
+ m_state.textBaseline = Context2D::Top;
+ else if (baseline == QLatin1String("bottom"))
+ m_state.textBaseline = Context2D::Bottom;
+ else if (baseline == QLatin1String("middle"))
+ m_state.textBaseline = Context2D::Middle;
+ else {
+ m_state.textBaseline = Context2D::Alphabetic;
+ qWarning() << (QLatin1String("Context2D: invalid baseline:") + baseline);
+ }
+ m_state.flags |= DirtyTextBaseline;
+}
+
+QString Context2D::textAlign()
+{
+ switch (m_state.textAlign) {
+ case Context2D::Left:
+ return QLatin1String("left");
+ case Context2D::Right:
+ return QLatin1String("right");
+ case Context2D::Center:
+ return QLatin1String("center");
+ case Context2D::Start:
+ return QLatin1String("start");
+ case Context2D::End:
+ return QLatin1String("end");
+ default:
+ Q_ASSERT("invalid value");
+ qWarning() << ("Context2D::invalid textAlign");
+ return QLatin1String("start");
+ }
+}
+
+void Context2D::setTextAlign(const QString &baseline)
+{
+ if (baseline==QLatin1String("start"))
+ m_state.textAlign = Context2D::Start;
+ else if (baseline == QLatin1String("end"))
+ m_state.textAlign = Context2D::End;
+ else if (baseline == QLatin1String("left"))
+ m_state.textAlign = Context2D::Left;
+ else if (baseline == QLatin1String("right"))
+ m_state.textAlign = Context2D::Right;
+ else if (baseline == QLatin1String("center"))
+ m_state.textAlign = Context2D::Center;
+ else {
+ m_state.textAlign= Context2D::Start;
+ qWarning("Context2D: invalid text align");
+ }
+ // ### alphabetic, ideographic, hanging
+ m_state.flags |= DirtyTextBaseline;
+}
+
+void Context2D::setFont(const QString &fontString)
+{
+ QFont font;
+ // ### this is simplified and incomplete
+ QStringList tokens = fontString.split(QLatin1Char(QLatin1Char(' ')));
+ foreach (const QString &token, tokens) {
+ if (token == QLatin1String("italic"))
+ font.setItalic(true);
+ else if (token == QLatin1String("bold"))
+ font.setBold(true);
+ else if (token.endsWith(QLatin1String("px"))) {
+ QString number = token;
+ number.remove(QLatin1String("px"));
+#ifdef Q_OS_MACX
+ // compensating the extra antialias space with bigger fonts
+ // this class is only used by the QML Profiler
+ // not much harm can be inflicted by this dirty hack
+ font.setPointSizeF(number.trimmed().toFloat()*4.0f/3.0f);
+#else
+ font.setPointSizeF(number.trimmed().toFloat());
+#endif
+ } else
+ font.setFamily(token);
+ }
+ m_state.font = font;
+ m_state.flags |= DirtyFont;
+}
+
+QString Context2D::font()
+{
+ return m_state.font.toString();
+}
+
+qreal Context2D::shadowOffsetX() const
+{
+ return m_state.shadowOffsetX;
+}
+
+qreal Context2D::shadowOffsetY() const
+{
+ return m_state.shadowOffsetY;
+}
+
+
+qreal Context2D::shadowBlur() const
+{
+ return m_state.shadowBlur;
+}
+
+
+QString Context2D::shadowColor() const
+{
+ return m_state.shadowColor.name();
+}
+
+
+void Context2D::clearRect(qreal x, qreal y, qreal w, qreal h)
+{
+ beginPainting();
+ m_painter.save();
+ m_painter.setMatrix(worldMatrix(), false);
+ m_painter.setCompositionMode(QPainter::CompositionMode_Source);
+ QColor fillColor = parent()->property("color").value<QColor>();
+
+ m_painter.fillRect(QRectF(x, y, w, h), fillColor);
+ m_painter.restore();
+ scheduleChange();
+}
+
+void Context2D::fillRect(qreal x, qreal y, qreal w, qreal h)
+{
+ beginPainting();
+ m_painter.save();
+ m_painter.setMatrix(worldMatrix(), false);
+ m_painter.fillRect(QRectF(x, y, w, h), m_painter.brush());
+ m_painter.restore();
+ scheduleChange();
+}
+
+int Context2D::baseLineOffset(Context2D::TextBaseLine value, const QFontMetrics &metrics)
+{
+ int offset = 0;
+ switch (value) {
+ case Context2D::Top:
+ break;
+ case Context2D::Alphabetic:
+ case Context2D::Middle:
+ case Context2D::Hanging:
+ offset = metrics.ascent();
+ break;
+ case Context2D::Bottom:
+ offset = metrics.height();
+ break;
+ }
+ return offset;
+}
+
+int Context2D::textAlignOffset(Context2D::TextAlign value, const QFontMetrics &metrics, const QString &text)
+{
+ int offset = 0;
+ if (value == Context2D::Start)
+ value = qApp->layoutDirection() == Qt::LeftToRight ? Context2D::Left : Context2D::Right;
+ else if (value == Context2D::End)
+ value = qApp->layoutDirection() == Qt::LeftToRight ? Context2D::Right: Context2D::Left;
+ switch (value) {
+ case Context2D::Center:
+ offset = metrics.width(text)/2;
+ break;
+ case Context2D::Right:
+ offset = metrics.width(text);
+ case Context2D::Left:
+ default:
+ break;
+ }
+ return offset;
+}
+
+void Context2D::fillText(const QString &text, qreal x, qreal y)
+{
+ beginPainting();
+ m_painter.save();
+ m_painter.setPen(QPen(m_state.fillStyle, m_state.lineWidth));
+ m_painter.setMatrix(worldMatrix(), false);
+ QFont font;
+ font.setBold(true);
+ m_painter.setFont(m_state.font);
+ int yoffset = baseLineOffset(m_state.textBaseline, m_painter.fontMetrics());
+ int xoffset = textAlignOffset(m_state.textAlign, m_painter.fontMetrics(), text);
+ QTextOption opt; // Adjust baseLine etc
+ m_painter.drawText(QRectF(x-xoffset, y-yoffset, QWIDGETSIZE_MAX, m_painter.fontMetrics().height()), text, opt);
+ m_painter.restore();
+ endPainting();
+ scheduleChange();
+}
+
+void Context2D::strokeText(const QString &text, qreal x, qreal y)
+{
+ beginPainting();
+ m_painter.save();
+ m_painter.setPen(QPen(m_state.fillStyle,0));
+ m_painter.setMatrix(worldMatrix(), false);
+
+ QPainterPath textPath;
+ QFont font = m_state.font;
+ font.setStyleStrategy(QFont::ForceOutline);
+ m_painter.setFont(font);
+ const QFontMetrics &metrics = m_painter.fontMetrics();
+ int yoffset = baseLineOffset(m_state.textBaseline, metrics);
+ int xoffset = textAlignOffset(m_state.textAlign, metrics, text);
+ textPath.addText(x-xoffset, y-yoffset+metrics.ascent(), font, text);
+ m_painter.strokePath(textPath, QPen(m_state.fillStyle, m_state.lineWidth));
+ m_painter.restore();
+ endPainting();
+ scheduleChange();
+}
+
+void Context2D::strokeRect(qreal x, qreal y, qreal w, qreal h)
+{
+ QPainterPath path;
+ path.addRect(x, y, w, h);
+ beginPainting();
+ m_painter.save();
+ m_painter.setMatrix(worldMatrix(), false);
+ m_painter.strokePath(path, m_painter.pen());
+ m_painter.restore();
+ scheduleChange();
+}
+
+void Context2D::mouseArea(qreal x, qreal y, qreal w, qreal h, const QScriptValue &callback,
+ const QScriptValue &data)
+{
+ MouseArea a = { callback, data, QRectF(x, y, w, h), m_state.matrix };
+ m_mouseAreas << a;
+}
+
+void Context2D::beginPath()
+{
+ m_path = QPainterPath();
+}
+
+
+void Context2D::closePath()
+{
+ m_path.closeSubpath();
+}
+
+
+void Context2D::moveTo(qreal x, qreal y)
+{
+ QPointF pt = worldMatrix().map(QPointF(x, y));
+ m_path.moveTo(pt);
+}
+
+
+void Context2D::lineTo(qreal x, qreal y)
+{
+ QPointF pt = worldMatrix().map(QPointF(x, y));
+ m_path.lineTo(pt);
+}
+
+
+void Context2D::quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y)
+{
+ QPointF cp = worldMatrix().map(QPointF(cpx, cpy));
+ QPointF xy = worldMatrix().map(QPointF(x, y));
+ m_path.quadTo(cp, xy);
+}
+
+
+void Context2D::bezierCurveTo(qreal cp1x, qreal cp1y,
+ qreal cp2x, qreal cp2y, qreal x, qreal y)
+{
+ QPointF cp1 = worldMatrix().map(QPointF(cp1x, cp1y));
+ QPointF cp2 = worldMatrix().map(QPointF(cp2x, cp2y));
+ QPointF end = worldMatrix().map(QPointF(x, y));
+ m_path.cubicTo(cp1, cp2, end);
+}
+
+
+void Context2D::arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius)
+{
+ //FIXME: this is surely busted
+ QPointF st = worldMatrix().map(QPointF(x1, y1));
+ QPointF end = worldMatrix().map(QPointF(x2, y2));
+ m_path.arcTo(st.x(), st.y(),
+ end.x()-st.x(), end.y()-st.y(),
+ radius, 90);
+}
+
+
+void Context2D::rect(qreal x, qreal y, qreal w, qreal h)
+{
+ QPainterPath path; path.addRect(x, y, w, h);
+ path = worldMatrix().map(path);
+ m_path.addPath(path);
+}
+
+void Context2D::arc(qreal xc, qreal yc, qreal radius,
+ qreal sar, qreal ear,
+ bool anticlockwise)
+{
+ //### HACK
+ // In Qt we don't switch the coordinate system for degrees
+ // and still use the 0,0 as bottom left for degrees so we need
+ // to switch
+ sar = -sar;
+ ear = -ear;
+ anticlockwise = !anticlockwise;
+ //end hack
+
+ float sa = DEGREES(sar);
+ float ea = DEGREES(ear);
+
+ double span = 0;
+
+ double xs = xc - radius;
+ double ys = yc - radius;
+ double width = radius*2;
+ double height = radius*2;
+
+ if (!anticlockwise && (ea < sa))
+ span += 360;
+ else if (anticlockwise && (sa < ea))
+ span -= 360;
+
+ //### this is also due to switched coordinate system
+ // we would end up with a 0 span instead of 360
+ if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) &&
+ qFuzzyCompare(qAbs(span), 360))) {
+ span += ea - sa;
+ }
+
+ QPainterPath path;
+ path.moveTo(QPointF(xc + radius * cos(sar),
+ yc - radius * sin(sar)));
+
+ path.arcTo(xs, ys, width, height, sa, span);
+ path = worldMatrix().map(path);
+ m_path.addPath(path);
+}
+
+
+void Context2D::fill()
+{
+ beginPainting();
+ m_painter.fillPath(m_path, m_painter.brush());
+ scheduleChange();
+}
+
+
+void Context2D::stroke()
+{
+ beginPainting();
+ m_painter.save();
+ m_painter.setMatrix(worldMatrix(), false);
+ QPainterPath tmp = worldMatrix().inverted().map(m_path);
+ m_painter.strokePath(tmp, m_painter.pen());
+ m_painter.restore();
+ scheduleChange();
+}
+
+
+void Context2D::clip()
+{
+ m_state.clipPath = m_path;
+ m_state.flags |= DirtyClippingRegion;
+}
+
+
+bool Context2D::isPointInPath(qreal x, qreal y) const
+{
+ return m_path.contains(QPointF(x, y));
+}
+
+
+ImageData Context2D::getImageData(qreal sx, qreal sy, qreal sw, qreal sh)
+{
+ Q_UNUSED(sx);
+ Q_UNUSED(sy);
+ Q_UNUSED(sw);
+ Q_UNUSED(sh);
+ return ImageData();
+}
+
+
+void Context2D::putImageData(ImageData image, qreal dx, qreal dy)
+{
+ Q_UNUSED(image);
+ Q_UNUSED(dx);
+ Q_UNUSED(dy);
+}
+
+Context2D::Context2D(QObject *parent)
+ : QObject(parent), m_changeTimerId(-1), m_width(0), m_height(0), m_inPaint(false)
+{
+ reset();
+}
+
+void Context2D::setupPainter()
+{
+ m_painter.setRenderHint(QPainter::Antialiasing, true);
+ if ((m_state.flags & DirtyClippingRegion) && !m_state.clipPath.isEmpty())
+ m_painter.setClipPath(m_state.clipPath);
+ if (m_state.flags & DirtyFillStyle)
+ m_painter.setBrush(m_state.fillStyle);
+ if (m_state.flags & DirtyGlobalAlpha)
+ m_painter.setOpacity(m_state.globalAlpha);
+ if (m_state.flags & DirtyGlobalCompositeOperation)
+ m_painter.setCompositionMode(m_state.globalCompositeOperation);
+ if (m_state.flags & MDirtyPen) {
+ QPen pen = m_painter.pen();
+ if (m_state.flags & DirtyStrokeStyle)
+ pen.setBrush(m_state.strokeStyle);
+ if (m_state.flags & DirtyLineWidth)
+ pen.setWidthF(m_state.lineWidth);
+ if (m_state.flags & DirtyLineCap)
+ pen.setCapStyle(m_state.lineCap);
+ if (m_state.flags & DirtyLineJoin)
+ pen.setJoinStyle(m_state.lineJoin);
+ if (m_state.flags & DirtyMiterLimit)
+ pen.setMiterLimit(m_state.miterLimit);
+ m_painter.setPen(pen);
+ }
+}
+
+void Context2D::beginPainting()
+{
+ if (m_width <= 0 || m_height <=0)
+ return;
+
+ if (m_pixmap.width() != m_width || m_pixmap.height() != m_height) {
+ if (m_painter.isActive())
+ m_painter.end();
+ m_pixmap = QPixmap(m_width, m_height);
+ m_pixmap.fill(parent()->property("color").value<QColor>());
+ }
+
+ if (m_state.shadowBlur > 0 && m_painter.device() != &m_shadowbuffer) {
+ if (m_painter.isActive())
+ m_painter.end();
+ updateShadowBuffer();
+ m_painter.begin(&m_shadowbuffer);
+ m_painter.setViewport(m_state.shadowOffsetX,
+ m_state.shadowOffsetY,
+ m_shadowbuffer.width(),
+ m_shadowbuffer.height());
+ m_shadowbuffer.fill(Qt::transparent);
+ }
+
+ if (!m_painter.isActive()) {
+ m_painter.begin(&m_pixmap);
+ m_painter.setRenderHint(QPainter::Antialiasing);
+ if (!m_state.clipPath.isEmpty())
+ m_painter.setClipPath(m_state.clipPath);
+ m_painter.setBrush(m_state.fillStyle);
+ m_painter.setOpacity(m_state.globalAlpha);
+ QPen pen;
+ pen.setBrush(m_state.strokeStyle);
+ if (pen.style() == Qt::NoPen)
+ pen.setStyle(Qt::SolidLine);
+ pen.setCapStyle(m_state.lineCap);
+ pen.setJoinStyle(m_state.lineJoin);
+ pen.setWidthF(m_state.lineWidth);
+ pen.setMiterLimit(m_state.miterLimit);
+ m_painter.setPen(pen);
+ } else {
+ setupPainter();
+ m_state.flags = 0;
+ }
+}
+
+void Context2D::endPainting()
+{
+ if (m_state.shadowBlur > 0) {
+ QImage alphaChannel = m_shadowbuffer.alphaChannel();
+
+ qt_blurImage(alphaChannel, m_state.shadowBlur, false, 1);
+
+ QRect imageRect = m_shadowbuffer.rect();
+
+ if (m_shadowColorIndexBuffer.isEmpty() || m_shadowColorBuffer != m_state.shadowColor) {
+ m_shadowColorIndexBuffer.clear();
+ m_shadowColorBuffer = m_state.shadowColor;
+
+ for (int i = 0; i < 256; ++i) {
+ m_shadowColorIndexBuffer << qRgba(qRound(255 * m_state.shadowColor.redF()),
+ qRound(255 * m_state.shadowColor.greenF()),
+ qRound(255 * m_state.shadowColor.blueF()),
+ i);
+ }
+ }
+ alphaChannel.setColorTable(m_shadowColorIndexBuffer);
+
+ if (m_painter.isActive())
+ m_painter.end();
+
+ m_painter.begin(&m_pixmap);
+
+ // draw the blurred drop shadow...
+ m_painter.save();
+ QTransform tf = m_painter.transform();
+ m_painter.translate(0, imageRect.height());
+ m_painter.rotate(-90);
+ m_painter.drawImage(0, 0, alphaChannel);
+ m_painter.setTransform(tf);
+ m_painter.restore();
+
+ // draw source
+ m_painter.drawImage(-m_state.shadowOffsetX, -m_state.shadowOffsetY, m_shadowbuffer.copy());
+ m_painter.end();
+ }
+}
+
+void Context2D::clear()
+{
+ m_painter.fillRect(QRect(QPoint(0,0), size()), Qt::white);
+}
+
+void Context2D::reset()
+{
+ m_stateStack.clear();
+ m_state.matrix = QMatrix();
+ m_state.clipPath = QPainterPath();
+ m_state.strokeStyle = Qt::black;
+ m_state.fillStyle = Qt::black;
+ m_state.globalAlpha = 1.0;
+ m_state.lineWidth = 1;
+ m_state.lineCap = Qt::FlatCap;
+ m_state.lineJoin = Qt::MiterJoin;
+ m_state.miterLimit = 10;
+ m_state.shadowOffsetX = 0;
+ m_state.shadowOffsetY = 0;
+ m_state.shadowBlur = 0;
+ m_state.shadowColor = qRgba(0, 0, 0, 0);
+ m_state.globalCompositeOperation = QPainter::CompositionMode_SourceOver;
+ m_state.font = QFont();
+ m_state.textAlign = Start;
+ m_state.textBaseline = Alphabetic;
+ m_state.flags = AllIsFullOfDirt;
+ m_mouseAreas.clear();
+ clear();
+}
+
+void Context2D::drawImage(const QVariant &var, qreal sx, qreal sy,
+ qreal sw = 0, qreal sh = 0)
+{
+ CanvasImage *image = qobject_cast<CanvasImage*>(var.value<QObject*>());
+ if (!image) {
+ Canvas *canvas = qobject_cast<Canvas*>(var.value<QObject*>());
+ if (canvas)
+ image = canvas->toImage();
+ }
+ if (image) {
+ beginPainting();
+ if (sw == sh && sh == 0)
+ m_painter.drawPixmap(QPointF(sx, sy), image->value());
+ else
+ m_painter.drawPixmap(QRect(sx, sy, sw, sh), image->value());
+
+ scheduleChange();
+ }
+}
+
+void Context2D::setSize(int width, int height)
+{
+ endPainting();
+ m_width = width;
+ m_height = height;
+
+ scheduleChange();
+}
+
+void Context2D::setSize(const QSize &size)
+{
+ setSize(size.width(), size.height());
+}
+
+QSize Context2D::size() const
+{
+ return m_pixmap.size();
+}
+
+QPoint Context2D::painterTranslate() const
+{
+ return m_painterTranslate;
+}
+
+void Context2D::setPainterTranslate(const QPoint &translate)
+{
+ m_painterTranslate = translate;
+ m_state.flags |= DirtyTransformationMatrix;
+}
+
+void Context2D::scheduleChange()
+{
+ if (m_changeTimerId == -1 && !m_inPaint)
+ m_changeTimerId = startTimer(0);
+}
+
+void Context2D::timerEvent(QTimerEvent *e)
+{
+ if (e->timerId() == m_changeTimerId) {
+ killTimer(m_changeTimerId);
+ m_changeTimerId = -1;
+ endPainting();
+ emit changed();
+ } else {
+ QObject::timerEvent(e);
+ }
+}
+
+QMatrix Context2D::worldMatrix() const
+{
+ QMatrix mat;
+ mat.translate(m_painterTranslate.x(), m_painterTranslate.y());
+ mat *= m_state.matrix;
+ return mat;
+}
+
+QT_END_NAMESPACE
diff --git a/plugins/qmlprofiler/canvas/qdeclarativecontext2d_p.h b/plugins/qmlprofiler/canvas/qdeclarativecontext2d_p.h
new file mode 100644
index 0000000000..7183875768
--- /dev/null
+++ b/plugins/qmlprofiler/canvas/qdeclarativecontext2d_p.h
@@ -0,0 +1,324 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVECONTEXT2D_P_H
+#define QDECLARATIVECONTEXT2D_P_H
+
+#include <qpainter.h>
+#include <qpainterpath.h>
+#include <qpixmap.h>
+#include <qstring.h>
+#include <qstack.h>
+#include <qmetatype.h>
+#include <qcoreevent.h>
+#include <qvariant.h>
+#include <qscriptvalue.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+QColor colorFromString(const QString &name);
+
+class CanvasGradient : public QObject
+{
+ Q_OBJECT
+public:
+ CanvasGradient(const QGradient &gradient) : m_gradient(gradient) {}
+
+public slots:
+ QGradient value() { return m_gradient; }
+ void addColorStop(float pos, const QString &color) { m_gradient.setColorAt(pos, colorFromString(color));}
+
+public:
+ QGradient m_gradient;
+};
+
+class CanvasImage: public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString src READ src WRITE setSrc NOTIFY sourceChanged)
+ Q_PROPERTY(int width READ width)
+ Q_PROPERTY(int height READ height)
+
+public:
+ CanvasImage() {}
+ CanvasImage(const QString &url) : m_image(url), m_src(url) {}
+ CanvasImage(const QPixmap &pixmap) {m_image = pixmap;}
+
+public slots:
+ int width() { return m_image.width(); }
+ int height() { return m_image.height(); }
+ QPixmap &value() { return m_image; }
+ QString src() { return m_src; }
+ void setSrc(const QString &src) { m_src = src; m_image.load(src); emit sourceChanged();}
+signals:
+ void sourceChanged();
+
+private:
+ QPixmap m_image;
+ QString m_src;
+};
+
+
+class ImageData {
+};
+
+class Context2D : public QObject
+{
+ Q_OBJECT
+ // compositing
+ Q_PROPERTY(qreal globalAlpha READ globalAlpha WRITE setGlobalAlpha)
+ Q_PROPERTY(QString globalCompositeOperation READ globalCompositeOperation WRITE setGlobalCompositeOperation)
+ Q_PROPERTY(QVariant strokeStyle READ strokeStyle WRITE setStrokeStyle)
+ Q_PROPERTY(QVariant fillStyle READ fillStyle WRITE setFillStyle)
+ // line caps/joins
+ Q_PROPERTY(qreal lineWidth READ lineWidth WRITE setLineWidth)
+ Q_PROPERTY(QString lineCap READ lineCap WRITE setLineCap)
+ Q_PROPERTY(QString lineJoin READ lineJoin WRITE setLineJoin)
+ Q_PROPERTY(qreal miterLimit READ miterLimit WRITE setMiterLimit)
+ // shadows
+ Q_PROPERTY(qreal shadowOffsetX READ shadowOffsetX WRITE setShadowOffsetX)
+ Q_PROPERTY(qreal shadowOffsetY READ shadowOffsetY WRITE setShadowOffsetY)
+ Q_PROPERTY(qreal shadowBlur READ shadowBlur WRITE setShadowBlur)
+ Q_PROPERTY(QString shadowColor READ shadowColor WRITE setShadowColor)
+ // fonts
+ Q_PROPERTY(QString font READ font WRITE setFont)
+ Q_PROPERTY(QString textBaseline READ textBaseline WRITE setTextBaseline)
+ Q_PROPERTY(QString textAlign READ textAlign WRITE setTextAlign)
+
+ enum TextBaseLine { Alphabetic=0, Top, Middle, Bottom, Hanging};
+ enum TextAlign { Start=0, End, Left, Right, Center};
+
+public:
+ Context2D(QObject *parent = 0);
+ void setSize(int width, int height);
+ void setSize(const QSize &size);
+ QSize size() const;
+
+ QPoint painterTranslate() const;
+ void setPainterTranslate(const QPoint &);
+
+ void scheduleChange();
+ void timerEvent(QTimerEvent *e);
+
+ void clear();
+ void reset();
+
+ QPixmap pixmap() { return m_pixmap; }
+
+ // compositing
+ qreal globalAlpha() const; // (default 1.0)
+ QString globalCompositeOperation() const; // (default over)
+ QVariant strokeStyle() const; // (default black)
+ QVariant fillStyle() const; // (default black)
+
+ void setGlobalAlpha(qreal alpha);
+ void setGlobalCompositeOperation(const QString &op);
+ void setStrokeStyle(const QVariant &style);
+ void setFillStyle(const QVariant &style);
+
+ // line caps/joins
+ qreal lineWidth() const; // (default 1)
+ QString lineCap() const; // "butt", "round", "square" (default "butt")
+ QString lineJoin() const; // "round", "bevel", "miter" (default "miter")
+ qreal miterLimit() const; // (default 10)
+
+ void setLineWidth(qreal w);
+ void setLineCap(const QString &s);
+ void setLineJoin(const QString &s);
+ void setMiterLimit(qreal m);
+
+ void setFont(const QString &font);
+ QString font();
+ void setTextBaseline(const QString &font);
+ QString textBaseline();
+ void setTextAlign(const QString &font);
+ QString textAlign();
+
+ // shadows
+ qreal shadowOffsetX() const; // (default 0)
+ qreal shadowOffsetY() const; // (default 0)
+ qreal shadowBlur() const; // (default 0)
+ QString shadowColor() const; // (default black)
+
+ void setShadowOffsetX(qreal x);
+ void setShadowOffsetY(qreal y);
+ void setShadowBlur(qreal b);
+ void setShadowColor(const QString &str);
+
+ struct MouseArea {
+ QScriptValue callback;
+ QScriptValue data;
+ QRectF rect;
+ QMatrix matrix;
+ };
+ const QList<MouseArea> &mouseAreas() const;
+
+public slots:
+ void save(); // push state on state stack
+ void restore(); // pop state stack and restore state
+
+ void fillText(const QString &text, qreal x, qreal y);
+ void strokeText(const QString &text, qreal x, qreal y);
+
+ void setInPaint(bool val){m_inPaint = val;}
+ void scale(qreal x, qreal y);
+ void rotate(qreal angle);
+ void translate(qreal x, qreal y);
+ void transform(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy);
+ void setTransform(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy);
+
+ CanvasGradient *createLinearGradient(qreal x0, qreal y0,
+ qreal x1, qreal y1);
+ CanvasGradient *createRadialGradient(qreal x0, qreal y0,
+ qreal r0, qreal x1,
+ qreal y1, qreal r1);
+
+ // rects
+ void clearRect(qreal x, qreal y, qreal w, qreal h);
+ void fillRect(qreal x, qreal y, qreal w, qreal h);
+ void strokeRect(qreal x, qreal y, qreal w, qreal h);
+
+ // mouse
+ void mouseArea(qreal x, qreal y, qreal w, qreal h, const QScriptValue &, const QScriptValue & = QScriptValue());
+
+ // path API
+ void beginPath();
+ void closePath();
+ void moveTo(qreal x, qreal y);
+ void lineTo(qreal x, qreal y);
+ void quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y);
+ void bezierCurveTo(qreal cp1x, qreal cp1y,
+ qreal cp2x, qreal cp2y, qreal x, qreal y);
+ void arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius);
+ void rect(qreal x, qreal y, qreal w, qreal h);
+ void arc(qreal x, qreal y, qreal radius,
+ qreal startAngle, qreal endAngle,
+ bool anticlockwise);
+ void fill();
+ void stroke();
+ void clip();
+ bool isPointInPath(qreal x, qreal y) const;
+
+ CanvasImage *createImage(const QString &url);
+
+ // drawing images (no overloads due to QTBUG-11604)
+ void drawImage(const QVariant &var, qreal dx, qreal dy, qreal dw, qreal dh);
+
+ // pixel manipulation
+ ImageData getImageData(qreal sx, qreal sy, qreal sw, qreal sh);
+ void putImageData(ImageData image, qreal dx, qreal dy);
+ void endPainting();
+
+signals:
+ void changed();
+
+private:
+ void setupPainter();
+ void beginPainting();
+ void updateShadowBuffer();
+
+ int m_changeTimerId;
+ QPainterPath m_path;
+
+ enum DirtyFlag {
+ DirtyTransformationMatrix = 0x00001,
+ DirtyClippingRegion = 0x00002,
+ DirtyStrokeStyle = 0x00004,
+ DirtyFillStyle = 0x00008,
+ DirtyGlobalAlpha = 0x00010,
+ DirtyLineWidth = 0x00020,
+ DirtyLineCap = 0x00040,
+ DirtyLineJoin = 0x00080,
+ DirtyMiterLimit = 0x00100,
+ MDirtyPen = DirtyStrokeStyle
+ | DirtyLineWidth
+ | DirtyLineCap
+ | DirtyLineJoin
+ | DirtyMiterLimit,
+ DirtyShadowOffsetX = 0x00200,
+ DirtyShadowOffsetY = 0x00400,
+ DirtyShadowBlur = 0x00800,
+ DirtyShadowColor = 0x01000,
+ DirtyGlobalCompositeOperation = 0x2000,
+ DirtyFont = 0x04000,
+ DirtyTextAlign = 0x08000,
+ DirtyTextBaseline = 0x10000,
+ AllIsFullOfDirt = 0xfffff
+ };
+
+ struct State {
+ State() : flags(0) {}
+ QMatrix matrix;
+ QPainterPath clipPath;
+ QBrush strokeStyle;
+ QBrush fillStyle;
+ qreal globalAlpha;
+ qreal lineWidth;
+ Qt::PenCapStyle lineCap;
+ Qt::PenJoinStyle lineJoin;
+ qreal miterLimit;
+ qreal shadowOffsetX;
+ qreal shadowOffsetY;
+ qreal shadowBlur;
+ QColor shadowColor;
+ QPainter::CompositionMode globalCompositeOperation;
+ QFont font;
+ Context2D::TextAlign textAlign;
+ Context2D::TextBaseLine textBaseline;
+ int flags;
+ };
+
+ int baseLineOffset(Context2D::TextBaseLine value, const QFontMetrics &metrics);
+ int textAlignOffset(Context2D::TextAlign value, const QFontMetrics &metrics, const QString &string);
+
+ QMatrix worldMatrix() const;
+
+ QPoint m_painterTranslate;
+ State m_state;
+ QStack<State> m_stateStack;
+ QPixmap m_pixmap;
+ QList<MouseArea> m_mouseAreas;
+ QImage m_shadowbuffer;
+ QVector<QRgb> m_shadowColorIndexBuffer;
+ QColor m_shadowColorBuffer;
+ QPainter m_painter;
+ int m_width, m_height;
+ bool m_inPaint;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(CanvasImage*)
+Q_DECLARE_METATYPE(CanvasGradient*)
+
+#endif // QDECLARATIVECONTEXT2D_P_H
diff --git a/plugins/qmlprofiler/canvas/qmlprofilercanvas.cpp b/plugins/qmlprofiler/canvas/qmlprofilercanvas.cpp
new file mode 100644
index 0000000000..01e3b3cdda
--- /dev/null
+++ b/plugins/qmlprofiler/canvas/qmlprofilercanvas.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilercanvas.h"
+
+#include "qdeclarativecontext2d_p.h"
+
+#include <qpixmap.h>
+#include <qpainter.h>
+
+namespace QmlProfiler {
+namespace Internal {
+
+QmlProfilerCanvas::QmlProfilerCanvas()
+ : m_context2d(new Context2D(this))
+ , m_dirty(true)
+{
+ setFlag(QGraphicsItem::ItemHasNoContents, false);
+ setAcceptedMouseButtons(Qt::LeftButton);
+ setCacheMode(QGraphicsItem::DeviceCoordinateCache);
+}
+
+void QmlProfilerCanvas::requestPaint()
+{
+ update();
+}
+
+void QmlProfilerCanvas::requestRedraw()
+{
+ setDirty(true);
+ update();
+}
+
+void QmlProfilerCanvas::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
+{
+ if (m_context2d->size().width() != width() || m_context2d->size().height() != height()) {
+ m_dirty = true;
+ m_context2d->setSize(width(), height());
+ }
+
+ if (m_dirty) {
+ m_context2d->reset();
+
+ emit drawRegion(m_context2d, QRect(0, 0, width(), height()));
+ setDirty(false);
+ }
+
+ p->drawPixmap(0, 0, m_context2d->pixmap());
+}
+
+void QmlProfilerCanvas::componentComplete()
+{
+ const QMetaObject *metaObject = this->metaObject();
+ int propertyCount = metaObject->propertyCount();
+ int requestPaintMethod = metaObject->indexOfMethod("requestPaint()");
+ for (int ii = QmlProfilerCanvas::staticMetaObject.propertyCount(); ii < propertyCount; ++ii) {
+ QMetaProperty p = metaObject->property(ii);
+ if (p.hasNotifySignal())
+ QMetaObject::connect(this, p.notifySignalIndex(), this, requestPaintMethod, 0, 0);
+ }
+ QDeclarativeItem::componentComplete();
+}
+
+}
+}
diff --git a/plugins/qmlprofiler/canvas/qmlprofilercanvas.h b/plugins/qmlprofiler/canvas/qmlprofilercanvas.h
new file mode 100644
index 0000000000..51289d8b96
--- /dev/null
+++ b/plugins/qmlprofiler/canvas/qmlprofilercanvas.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERCANVAS_H
+#define QMLPROFILERCANVAS_H
+
+#include <QDeclarativeItem>
+
+QT_BEGIN_NAMESPACE
+class Context2D;
+QT_END_NAMESPACE
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerCanvas : public QDeclarativeItem
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged)
+
+public:
+ QmlProfilerCanvas();
+
+ bool dirty() const { return m_dirty; }
+ void setDirty(bool dirty)
+ {
+ if (m_dirty != dirty) {
+ m_dirty = dirty;
+ emit dirtyChanged(dirty);
+ }
+ }
+
+signals:
+ void dirtyChanged(bool dirty);
+
+ void drawRegion(Context2D *ctxt, const QRect &region);
+
+public slots:
+ void requestPaint();
+ void requestRedraw();
+
+protected:
+ virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
+ virtual void componentComplete();
+
+private:
+ Context2D *m_context2d;
+
+ bool m_dirty;
+};
+
+}
+}
+
+#endif // QMLPROFILERCANVAS_H
diff --git a/plugins/qmlprofiler/localqmlprofilerrunner.cpp b/plugins/qmlprofiler/localqmlprofilerrunner.cpp
new file mode 100644
index 0000000000..d6b3dccf97
--- /dev/null
+++ b/plugins/qmlprofiler/localqmlprofilerrunner.cpp
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "localqmlprofilerrunner.h"
+#include "qmlprofilerplugin.h"
+
+using namespace QmlProfiler;
+using namespace QmlProfiler::Internal;
+
+LocalQmlProfilerRunner::LocalQmlProfilerRunner(const Configuration &configuration, QObject *parent) :
+ AbstractQmlProfilerRunner(parent),
+ m_configuration(configuration)
+{
+ connect(&m_launcher, SIGNAL(appendMessage(QString,Utils::OutputFormat)),
+ this, SIGNAL(appendMessage(QString,Utils::OutputFormat)));
+}
+
+void LocalQmlProfilerRunner::start()
+{
+ QString arguments = QString::fromLatin1("-qmljsdebugger=port:%1,block").arg(m_configuration.port);
+
+ if (!m_configuration.executableArguments.isEmpty())
+ arguments += QLatin1Char(' ') + m_configuration.executableArguments;
+
+ if (QmlProfilerPlugin::debugOutput)
+ qWarning("QmlProfiler: Launching %s:%d", qPrintable(m_configuration.executable),
+ m_configuration.port);
+
+ m_launcher.setWorkingDirectory(m_configuration.workingDirectory);
+ m_launcher.setEnvironment(m_configuration.environment);
+ connect(&m_launcher, SIGNAL(processExited(int)), this, SLOT(spontaneousStop(int)));
+ m_launcher.start(ProjectExplorer::ApplicationLauncher::Gui, m_configuration.executable,
+ arguments);
+
+ emit started();
+}
+
+void LocalQmlProfilerRunner::spontaneousStop(int exitCode)
+{
+ if (QmlProfilerPlugin::debugOutput)
+ qWarning("QmlProfiler: Application exited (exit code %d).", exitCode);
+
+ disconnect(&m_launcher, SIGNAL(processExited(int)), this, SLOT(spontaneousStop(int)));
+
+ emit stopped();
+}
+
+void LocalQmlProfilerRunner::stop()
+{
+ if (QmlProfilerPlugin::debugOutput)
+ qWarning("QmlProfiler: Stopping application ...");
+
+ if (m_launcher.isRunning())
+ m_launcher.stop();
+}
+
+quint16 LocalQmlProfilerRunner::debugPort() const
+{
+ return m_configuration.port;
+}
diff --git a/plugins/qmlprofiler/localqmlprofilerrunner.h b/plugins/qmlprofiler/localqmlprofilerrunner.h
new file mode 100644
index 0000000000..042df14625
--- /dev/null
+++ b/plugins/qmlprofiler/localqmlprofilerrunner.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef LOCALQMLPROFILERRUNNER_H
+#define LOCALQMLPROFILERRUNNER_H
+
+#include "abstractqmlprofilerrunner.h"
+
+#include <utils/environment.h>
+#include <projectexplorer/applicationlauncher.h>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class LocalQmlProfilerRunner : public AbstractQmlProfilerRunner
+{
+ Q_OBJECT
+
+public:
+ struct Configuration {
+ QString executable;
+ QString executableArguments;
+ quint16 port;
+ QString workingDirectory;
+ Utils::Environment environment;
+ };
+
+ explicit LocalQmlProfilerRunner(const Configuration &configuration, QObject *parent = 0);
+
+ // AbstractQmlProfilerRunner
+ virtual void start();
+ virtual void stop();
+ virtual quint16 debugPort() const;
+
+ bool hasExecutable() const { return !m_configuration.executable.isEmpty(); }
+
+private slots:
+ void spontaneousStop(int exitCode);
+
+private:
+ Configuration m_configuration;
+ ProjectExplorer::ApplicationLauncher m_launcher;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // LOCALQMLPROFILERRUNNER_H
diff --git a/plugins/qmlprofiler/qml/Detail.qml b/plugins/qmlprofiler/qml/Detail.qml
new file mode 100644
index 0000000000..0431b9bd51
--- /dev/null
+++ b/plugins/qmlprofiler/qml/Detail.qml
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+import QtQuick 1.0
+import Monitor 1.0
+
+Item {
+ id: detail
+ property string label
+ property string content
+
+ height: childrenRect.height+2
+ width: childrenRect.width
+ Item {
+ id: guideline
+ x: 70
+ width: 5
+ }
+ Text {
+ y: 1
+ id: lbl
+ text: label
+ font.pixelSize: 12
+ font.bold: true
+ }
+ Text {
+ text: content
+ font.pixelSize: 12
+ anchors.baseline: lbl.baseline
+ anchors.left: guideline.right
+ }
+}
diff --git a/plugins/qmlprofiler/qml/Label.qml b/plugins/qmlprofiler/qml/Label.qml
new file mode 100644
index 0000000000..71b349ca1d
--- /dev/null
+++ b/plugins/qmlprofiler/qml/Label.qml
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+import QtQuick 1.0
+
+Item {
+ id: labelContainer
+ property alias text: txt.text
+ property bool expanded: false
+ property int typeIndex: index
+
+ property variant descriptions: []
+ property variant extdescriptions: []
+ property variant eventIds: []
+
+ height: root.singleRowHeight
+ width: 150
+
+ visible: !qmlProfilerModelProxy.empty;
+
+ onExpandedChanged: {
+ qmlProfilerModelProxy.setExpanded(typeIndex, expanded);
+ var rE = labels.rowExpanded;
+ rE[typeIndex] = expanded;
+ labels.rowExpanded = rE;
+ backgroundMarks.requestRedraw();
+ view.setRowExpanded(typeIndex, expanded);
+ getDescriptions();
+ updateHeight();
+ }
+
+ Component.onCompleted: {
+ updateHeight();
+ }
+
+ function updateHeight() {
+ height = root.singleRowHeight * qmlProfilerModelProxy.categoryDepth(typeIndex);
+ /*
+ height = root.singleRowHeight * (1 +
+ (expanded ? qmlProfilerDataModel.uniqueEventsOfType(typeIndex) :
+ qmlProfilerDataModel.maxNestingForType(typeIndex)));
+ */
+ }
+
+ function getDescriptions() {
+ var desc=[];
+ var ids=[];
+ var extdesc=[];
+ var labelList = qmlProfilerModelProxy.getLabelsForCategory(typeIndex);
+ for (var i = 0; i < labelList.length; i++ ) {
+ desc[i] = labelList[i].description;
+ ids[i] = labelList[i].id;
+ extdesc[i] = labelList[i].displayName + ":" + labelList[i].description;
+ }
+ descriptions = desc;
+ eventIds = ids;
+ extdescriptions = extdesc;
+ updateHeight();
+ }
+
+ /*
+ Connections {
+ target: qmlProfilerDataModel
+ onReloadDetailLabels: getDescriptions();
+ onStateChanged: {
+ // Empty
+ if (qmlProfilerDataModel.getCurrentStateFromQml() == 0) {
+ descriptions = [];
+ eventIds = [];
+ extdescriptions = [];
+ updateHeight();
+ } else
+ // Done
+ if (qmlProfilerDataModel.getCurrentStateFromQml() == 3) {
+ getDescriptions();
+ }
+ }
+ }
+ */
+ Connections {
+ target: qmlProfilerModelProxy
+// onReloadDetailLabels: getDescriptions();
+ onExpandedChanged: {
+ updateHeight();
+ }
+
+ onStateChanged: {
+ getDescriptions();
+// // Empty
+// if (qmlProfilerDataModel.getCurrentStateFromQml() == 0) {
+// descriptions = [];
+// eventIds = [];
+// extdescriptions = [];
+// updateHeight();
+// } else
+// // Done
+// if (qmlProfilerDataModel.getCurrentStateFromQml() == 3) {
+
+// }
+ }
+ }
+
+ Text {
+ id: txt
+ x: 5
+ font.pixelSize: 12
+ color: "#232323"
+ height: root.singleRowHeight
+ width: 140
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ Rectangle {
+ height: 1
+ width: parent.width
+ color: "#999999"
+ anchors.bottom: parent.bottom
+ z: 2
+ }
+
+ Column {
+ y: root.singleRowHeight
+ visible: expanded
+ Repeater {
+ model: descriptions.length
+ Rectangle {
+ width: labelContainer.width
+ height: root.singleRowHeight
+ color: "#eaeaea"
+ border.width: 1
+ border.color:"#c8c8c8"
+ Text {
+ height: root.singleRowHeight
+ x: 5
+ width: 140
+ text: descriptions[index]
+ elide: Text.ElideRight
+ verticalAlignment: Text.AlignVCenter
+ }
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ onEntered: changeToolTip(extdescriptions[index]);
+ onExited: changeToolTip("");
+ onClicked: {
+ if (mouse.modifiers & Qt.ShiftModifier)
+ view.selectPrevFromId(eventIds[index]);
+ else
+ view.selectNextFromId(eventIds[index]);
+ }
+ }
+ }
+ }
+ }
+
+ Image {
+ //visible: descriptions.length > 0
+ visible: true
+ source: expanded ? "arrow_down.png" : "arrow_right.png"
+ x: parent.width - 12
+ y: root.singleRowHeight / 2 - height / 2
+ MouseArea {
+ anchors.fill: parent
+ anchors.rightMargin: -10
+ anchors.leftMargin: -10
+ anchors.topMargin: -10
+ anchors.bottomMargin: -10
+ onClicked: {
+ expanded = !expanded;
+ }
+ }
+ }
+}
diff --git a/plugins/qmlprofiler/qml/MainView.qml b/plugins/qmlprofiler/qml/MainView.qml
new file mode 100644
index 0000000000..450f35d5eb
--- /dev/null
+++ b/plugins/qmlprofiler/qml/MainView.qml
@@ -0,0 +1,612 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+import QtQuick 1.0
+import Monitor 1.0
+
+Rectangle {
+ id: root
+
+ // ***** properties
+
+ property int candidateHeight: 0
+ property int scrollY: 0
+ height: Math.max( candidateHeight, labels.height + 2 )
+
+ property int singleRowHeight: 30
+
+ property bool dataAvailable: true
+ property int eventCount: 0
+ property real progress: 0
+
+ property alias selectionLocked : view.selectionLocked
+ signal updateLockButton
+ property alias selectedItem: view.selectedItem
+ signal selectedEventChanged(int eventId)
+ property bool lockItemSelection : false
+
+/* property variant names: [ qsTr("Painting"),
+ qsTr("Compiling"),
+ qsTr("Creating"),
+ qsTr("Binding"),
+ qsTr("Handling Signal")]
+ property variant colors : [ "#99CCB3", "#99CCCC", "#99B3CC",
+ "#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ]
+ */
+
+ property variant mainviewTimePerPixel : 0
+
+ signal updateCursorPosition
+ property string fileName: ""
+ property int lineNumber: -1
+ property int columnNumber: 0
+
+ signal updateRangeButton
+ property bool selectionRangeMode: false
+
+ property bool selectionRangeReady: selectionRange.ready
+ property variant selectionRangeStart: selectionRange.startTime
+ property variant selectionRangeEnd: selectionRange.startTime + selectionRange.duration
+
+ signal changeToolTip(string text)
+ signal updateVerticalScroll(int newPosition)
+
+ property bool recordingEnabled: false
+ property bool appKilled : false
+
+ property date recordingStartDate
+ property real elapsedTime
+
+ // ***** connections with external objects
+ Connections {
+ target: zoomControl
+ onRangeChanged: {
+ var startTime = zoomControl.startTime();
+ var endTime = zoomControl.endTime();
+ var duration = Math.abs(endTime - startTime);
+
+ mainviewTimePerPixel = duration / root.width;
+
+ backgroundMarks.updateMarks(startTime, endTime);
+ view.updateFlickRange(startTime, endTime);
+ if (duration > 0) {
+ var candidateWidth = qmlProfilerModelProxy.traceDuration() *
+ flick.width / duration;
+ if (flick.contentWidth !== candidateWidth)
+ flick.contentWidth = candidateWidth;
+ }
+
+ }
+ }
+
+
+ Connections {
+ target: qmlProfilerModelProxy
+ onCountChanged: {
+ eventCount = qmlProfilerModelProxy.count();
+ if (eventCount === 0)
+ root.clearAll();
+ if (eventCount > 1) {
+ root.progress = Math.min(1.0,
+ (qmlProfilerModelProxy.lastTimeMark() -
+ qmlProfilerModelProxy.traceStartTime()) / root.elapsedTime * 1e-9 );
+ } else {
+ root.progress = 0;
+ }
+ }
+ onStateChanged: {
+ switch (qmlProfilerModelProxy.getState()) {
+ case 0: {
+ root.clearAll();
+ break;
+ }
+ case 1: {
+ root.dataAvailable = false;
+ break;
+ }
+ case 2: {
+ root.progress = 0.9; // jump to 90%
+ break;
+ }
+ }
+ }
+ onDataAvailable: {
+ view.clearData();
+ zoomControl.setRange(0,0);
+ progress = 1.0;
+ dataAvailable = true;
+ view.visible = true;
+ view.requestPaint();
+ zoomControl.setRange(qmlProfilerModelProxy.traceStartTime(),
+ qmlProfilerModelProxy.traceStartTime() +
+ qmlProfilerModelProxy.traceDuration()/10);
+ }
+ }
+
+
+ // ***** functions
+ function gotoSourceLocation(file,line,column) {
+ root.fileName = file;
+ root.lineNumber = line;
+ root.columnNumber = column;
+ root.updateCursorPosition();
+ }
+
+ function clearData() {
+ view.clearData();
+ dataAvailable = false;
+ appKilled = false;
+ eventCount = 0;
+ hideRangeDetails();
+ selectionRangeMode = false;
+ updateRangeButton();
+ zoomControl.setRange(0,0);
+ }
+
+ function clearDisplay() {
+ clearData();
+ view.visible = false;
+ }
+
+ function clearAll() {
+ clearDisplay();
+ elapsedTime = 0;
+ }
+
+ function nextEvent() {
+ view.selectNext();
+ }
+
+ function prevEvent() {
+ view.selectPrev();
+ }
+
+ function updateWindowLength(absoluteFactor) {
+ var windowLength = view.endTime - view.startTime;
+ if (qmlProfilerModelProxy.traceEndTime() <= qmlProfilerModelProxy.traceStartTime() ||
+ windowLength <= 0)
+ return;
+ var currentFactor = windowLength / qmlProfilerModelProxy.traceDuration();
+ updateZoom(absoluteFactor / currentFactor);
+ }
+
+ function updateZoom(relativeFactor) {
+ var min_length = 1e5; // 0.1 ms
+ var windowLength = view.endTime - view.startTime;
+ if (windowLength < min_length)
+ windowLength = min_length;
+ var newWindowLength = windowLength * relativeFactor;
+
+ if (newWindowLength > qmlProfilerModelProxy.traceDuration()) {
+ newWindowLength = qmlProfilerModelProxy.traceDuration();
+ relativeFactor = newWindowLength / windowLength;
+ }
+ if (newWindowLength < min_length) {
+ newWindowLength = min_length;
+ relativeFactor = newWindowLength / windowLength;
+ }
+
+ var fixedPoint = (view.startTime + view.endTime) / 2;
+
+ if (view.selectedItem !== -1) {
+ // center on selected item if it's inside the current screen
+ var newFixedPoint = qmlProfilerModelProxy.getStartTime(view.selectedItem);
+ if (newFixedPoint >= view.startTime && newFixedPoint < view.endTime)
+ fixedPoint = newFixedPoint;
+ }
+
+
+ var startTime = fixedPoint - relativeFactor*(fixedPoint - view.startTime);
+ zoomControl.setRange(startTime, startTime + newWindowLength);
+ }
+
+ function updateZoomCentered(centerX, relativeFactor)
+ {
+ var min_length = 1e5; // 0.1 ms
+ var windowLength = view.endTime - view.startTime;
+ if (windowLength < min_length)
+ windowLength = min_length;
+ var newWindowLength = windowLength * relativeFactor;
+
+ if (newWindowLength > qmlProfilerModelProxy.traceDuration()) {
+ newWindowLength = qmlProfilerModelProxy.traceDuration();
+ relativeFactor = newWindowLength / windowLength;
+ }
+ if (newWindowLength < min_length) {
+ newWindowLength = min_length;
+ relativeFactor = newWindowLength / windowLength;
+ }
+
+ var fixedPoint = (centerX - flick.x) * windowLength / flick.width + view.startTime;
+ var startTime = fixedPoint - relativeFactor*(fixedPoint - view.startTime);
+ zoomControl.setRange(startTime, startTime + newWindowLength);
+ }
+
+ function recenter( centerPoint ) {
+ var windowLength = view.endTime - view.startTime;
+ var newStart = Math.floor(centerPoint - windowLength/2);
+ if (newStart < 0)
+ newStart = 0;
+ if (newStart + windowLength > qmlProfilerModelProxy.traceEndTime())
+ newStart = qmlProfilerModelProxy.traceEndTime() - windowLength;
+ zoomControl.setRange(newStart, newStart + windowLength);
+ }
+
+ function recenterOnItem( itemIndex )
+ {
+ if (itemIndex === -1)
+ return;
+
+ // if item is outside of the view, jump back to its position
+ if (qmlProfilerModelProxy.getEndTime(itemIndex) < view.startTime ||
+ qmlProfilerModelProxy.getStartTime(itemIndex) > view.endTime) {
+ recenter((qmlProfilerModelProxy.getStartTime(itemIndex) +
+ qmlProfilerModelProxy.getEndTime(itemIndex)) / 2);
+ }
+
+ }
+
+ function wheelZoom(wheelCenter, wheelDelta) {
+ if (qmlProfilerModelProxy.traceEndTime() > qmlProfilerModelProxy.traceStartTime() &&
+ wheelDelta !== 0) {
+ if (wheelDelta>0)
+ updateZoomCentered(wheelCenter, 1/1.2);
+ else
+ updateZoomCentered(wheelCenter, 1.2);
+ }
+ }
+
+ function hideRangeDetails() {
+ rangeDetails.visible = false;
+ rangeDetails.duration = "";
+ rangeDetails.label = "";
+ //rangeDetails.type = "";
+ rangeDetails.file = "";
+ rangeDetails.line = -1;
+ rangeDetails.column = 0;
+ rangeDetails.isBindingLoop = false;
+ }
+
+ function selectNextWithId( eventId )
+ {
+ if (!lockItemSelection) {
+ lockItemSelection = true;
+ var itemIndex = view.nextItemFromId( eventId );
+ // select an item, lock to it, and recenter if necessary
+ if (view.selectedItem != itemIndex) {
+ view.selectedItem = itemIndex;
+ if (itemIndex !== -1) {
+ view.selectionLocked = true;
+ recenterOnItem(itemIndex);
+ }
+ }
+ lockItemSelection = false;
+ }
+ }
+
+ // ***** slots
+ onSelectionRangeModeChanged: {
+ selectionRangeControl.enabled = selectionRangeMode;
+ selectionRange.reset(selectionRangeMode);
+ }
+
+ onSelectionLockedChanged: {
+ updateLockButton();
+ }
+
+ onSelectedItemChanged: {
+ if (selectedItem != -1 && !lockItemSelection) {
+ lockItemSelection = true;
+ /*
+ selectedEventChanged( qmlProfilerDataModel.getEventId(selectedItem) );
+ */
+ lockItemSelection = false;
+ }
+ }
+
+ onRecordingEnabledChanged: {
+ if (recordingEnabled) {
+ recordingStartDate = new Date();
+ elapsedTime = 0;
+ } else {
+ elapsedTime = (new Date() - recordingStartDate)/1000.0;
+ }
+ }
+
+
+ // ***** child items
+ TimeMarks {
+ id: backgroundMarks
+ y: labels.y
+ height: flick.height
+ anchors.left: flick.left
+ anchors.right: flick.right
+ }
+
+ Flickable {
+ id: flick
+ anchors.top: parent.top
+ anchors.topMargin: labels.y
+ anchors.right: parent.right
+ anchors.left: labels.right
+ height: root.height
+ contentWidth: 0;
+ contentHeight: labels.height
+ flickableDirection: Flickable.HorizontalFlick
+
+ onContentXChanged: {
+ if (Math.round(view.startX) !== contentX)
+ view.startX = contentX;
+ }
+
+ clip:true
+
+ MouseArea {
+ id: selectionRangeDrag
+ enabled: selectionRange.ready
+ anchors.fill: selectionRange
+ drag.target: selectionRange
+ drag.axis: "XAxis"
+ drag.minimumX: 0
+ drag.maximumX: flick.contentWidth - selectionRange.width
+ onPressed: {
+ selectionRange.isDragging = true;
+ }
+ onReleased: {
+ selectionRange.isDragging = false;
+ }
+ onDoubleClicked: {
+ zoomControl.setRange(selectionRange.startTime,
+ selectionRange.startTime + selectionRange.duration);
+ root.selectionRangeMode = false;
+ root.updateRangeButton();
+ }
+ }
+
+
+ SelectionRange {
+ id: selectionRange
+ visible: root.selectionRangeMode
+ height: root.height
+ z: 2
+ }
+
+ TimelineRenderer {
+ id: view
+
+ profilerModelProxy: qmlProfilerModelProxy
+
+ x: flick.contentX
+ width: flick.width
+ height: root.height
+
+ property variant startX: 0
+ onStartXChanged: {
+ var newStartTime = Math.round(startX * (endTime - startTime) / flick.width) +
+ qmlProfilerModelProxy.traceStartTime();
+ if (Math.abs(newStartTime - startTime) > 1) {
+ var newEndTime = Math.round((startX+flick.width) *
+ (endTime - startTime) /
+ flick.width) +
+ qmlProfilerModelProxy.traceStartTime();
+ zoomControl.setRange(newStartTime, newEndTime);
+ }
+
+ if (Math.round(startX) !== flick.contentX)
+ flick.contentX = startX;
+ }
+
+ function updateFlickRange(start, end) {
+ if (start !== startTime || end !== endTime) {
+ startTime = start;
+ endTime = end;
+ var newStartX = (startTime - qmlProfilerModelProxy.traceStartTime()) *
+ flick.width / (endTime-startTime);
+ if (Math.abs(newStartX - startX) >= 1)
+ startX = newStartX;
+ }
+ }
+
+ onSelectedItemChanged: {
+ if (selectedItem !== -1) {
+ // display details
+ /*
+ rangeDetails.duration = qmlProfilerDataModel.getDuration(selectedItem)/1000.0;
+ rangeDetails.label = qmlProfilerDataModel.getDetails(selectedItem);
+ rangeDetails.file = qmlProfilerDataModel.getFilename(selectedItem);
+ rangeDetails.line = qmlProfilerDataModel.getLine(selectedItem);
+ rangeDetails.column = qmlProfilerDataModel.getColumn(selectedItem);
+ rangeDetails.type = root.names[qmlProfilerDataModel.getType(selectedItem)];
+ rangeDetails.isBindingLoop = qmlProfilerDataModel.getBindingLoopDest(selectedItem)!==-1;
+
+ rangeDetails.visible = true;
+*/
+ rangeDetails.showInfo(qmlProfilerModelProxy.getEventDetails(selectedItem));
+
+ // center view (horizontally)
+ var windowLength = view.endTime - view.startTime;
+ var eventStartTime = qmlProfilerModelProxy.getStartTime(selectedItem);
+ var eventEndTime = eventStartTime +
+ qmlProfilerModelProxy.getDuration(selectedItem);
+
+ if (eventEndTime < view.startTime || eventStartTime > view.endTime) {
+ var center = (eventStartTime + eventEndTime)/2;
+ var from = Math.min(qmlProfilerModelProxy.traceEndTime()-windowLength,
+ Math.max(0, Math.floor(center - windowLength/2)));
+
+ zoomControl.setRange(from, from + windowLength);
+
+ }
+
+ // center view (vertically)
+ var itemY = view.getYPosition(selectedItem);
+ if (itemY < root.scrollY) {
+ root.updateVerticalScroll(itemY);
+ } else
+ if (itemY + root.singleRowHeight >
+ root.scrollY + root.candidateHeight) {
+ root.updateVerticalScroll(itemY + root.singleRowHeight -
+ root.candidateHeight);
+ }
+
+ } else {
+ root.hideRangeDetails();
+ }
+ }
+
+ onItemPressed: {
+ if (pressedItem !== -1) {
+ /*
+ root.gotoSourceLocation(qmlProfilerDataModel.getFilename(pressedItem),
+ qmlProfilerDataModel.getLine(pressedItem),
+ qmlProfilerDataModel.getColumn(pressedItem));
+ */
+ }
+ }
+
+ // hack to pass mouse events to the other mousearea if enabled
+ startDragArea: selectionRangeDrag.enabled ? selectionRangeDrag.x :
+ -flick.contentX
+ endDragArea: selectionRangeDrag.enabled ?
+ selectionRangeDrag.x + selectionRangeDrag.width :
+ -flick.contentX-1
+ }
+ MouseArea {
+ id: selectionRangeControl
+ enabled: false
+ width: flick.width
+ height: root.height
+ x: flick.contentX
+ hoverEnabled: enabled
+ z: 2
+
+ onReleased: {
+ selectionRange.releasedOnCreation();
+ }
+ onPressed: {
+ selectionRange.pressedOnCreation();
+ }
+ onMousePositionChanged: {
+ selectionRange.movedOnCreation();
+ }
+ }
+ }
+
+ SelectionRangeDetails {
+ id: selectionRangeDetails
+ visible: root.selectionRangeMode
+ startTime: selectionRange.startTimeString
+ duration: selectionRange.durationString
+ endTime: selectionRange.endTimeString
+ showDuration: selectionRange.width > 1
+ }
+
+ RangeDetails {
+ id: rangeDetails
+ }
+
+ Rectangle {
+ id: labels
+ width: 150
+ color: "#dcdcdc"
+ height: col.height
+
+ // TODO: this must go away
+ property int rowCount: 5
+ property variant rowExpanded: [false,false,false,false,false];
+
+ Column {
+ id: col
+ Repeater {
+ model: labels.rowCount
+ delegate: Label {
+ /*text: root.names[index] */
+ text: qmlProfilerModelProxy.categoryLabel(index)
+ height: labels.height/labels.rowCount
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ id: labelsTail
+ anchors.top: labels.bottom
+ anchors.bottom: root.bottom
+ width: labels.width
+ color: labels.color
+ }
+
+ // Gradient borders
+ Item {
+ anchors.left: labels.right
+ width: 6
+ anchors.top: root.top
+ anchors.bottom: root.bottom
+ Rectangle {
+ x: parent.width
+ transformOrigin: Item.TopLeft
+ rotation: 90
+ width: parent.height
+ height: parent.width
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "#00000000"; }
+ GradientStop { position: 1.0; color: "#86000000"; }
+ }
+ }
+ }
+
+ Item {
+ anchors.right: root.right
+ width: 6
+ anchors.top: root.top
+ anchors.bottom: root.bottom
+ Rectangle {
+ x: parent.width
+ transformOrigin: Item.TopLeft
+ rotation: 90
+ width: parent.height
+ height: parent.width
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "#86000000"; }
+ GradientStop { position: 1.0; color: "#00000000"; }
+ }
+ }
+ }
+
+ Rectangle {
+ y: root.scrollY + root.candidateHeight - height
+ height: 6
+ width: root.width
+ x: 0
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "#00000000"; }
+ GradientStop { position: 1.0; color: "#86000000"; }
+ }
+ }
+}
diff --git a/plugins/qmlprofiler/qml/Overview.js b/plugins/qmlprofiler/qml/Overview.js
new file mode 100644
index 0000000000..a334359189
--- /dev/null
+++ b/plugins/qmlprofiler/qml/Overview.js
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+.pragma library
+
+var qmlProfilerModelProxy = 0;
+
+//draw background of the graph
+function drawGraph(canvas, ctxt, region)
+{
+ ctxt.fillStyle = "#eaeaea";
+ ctxt.fillRect(0, 0, canvas.width, canvas.height);
+}
+
+//draw the actual data to be graphed
+function drawData(canvas, ctxt, region)
+{
+ if ((!qmlProfilerModelProxy) || qmlProfilerModelProxy.count() == 0)
+ return;
+
+ var typeCount = 5;
+ var width = canvas.width;
+ var bump = 10;
+ var height = canvas.height - bump;
+ var blockHeight = height / typeCount;
+
+ var spacing = width / qmlProfilerModelProxy.traceDuration();
+
+ var highest = [0,0,0,0,0]; // note: change if typeCount changes
+
+ for (var ii = 0; ii < qmlProfilerModelProxy.count(); ++ii) {
+
+ var xx = (qmlProfilerModelProxy.getStartTime(ii) -
+ qmlProfilerModelProxy.traceStartTime()) * spacing;
+ if (xx > region.x + region.width)
+ continue;
+
+ var eventWidth = qmlProfilerModelProxy.getDuration(ii) * spacing;
+ if (xx + eventWidth < region.x)
+ continue;
+
+ if (eventWidth < 1)
+ eventWidth = 1;
+
+ xx = Math.round(xx);
+ var ty = qmlProfilerModelProxy.getType(ii);
+
+ if (xx + eventWidth > highest[ty]) {
+ /*
+ // special: animations
+ if (ty === 0 && qmlProfilerModelProxy.getAnimationCount(ii) >= 0) {
+ var vertScale = qmlProfilerModelProxy.getMaximumAnimationCount() -
+ qmlProfilerModelProxy.getMinimumAnimationCount();
+ if (vertScale < 1)
+ vertScale = 1;
+ var fraction = (qmlProfilerModelProxy.getAnimationCount(ii) -
+ qmlProfilerModelProxy.getMinimumAnimationCount()) / vertScale;
+ var eventHeight = blockHeight * (fraction * 0.85 + 0.15);
+ var yy = bump + ty*blockHeight + blockHeight - eventHeight;
+
+ var fpsFraction = qmlProfilerModelProxy.getFramerate(ii) / 60.0;
+ if (fpsFraction > 1.0)
+ fpsFraction = 1.0;
+ ctxt.fillStyle = "hsl("+(fpsFraction*0.27+0.028)+",0.3,0.65)";
+ ctxt.fillRect(xx, yy, eventWidth, eventHeight);
+ } else { */
+ var hue = ( qmlProfilerModelProxy.getEventId(ii) * 25 ) % 360;
+ ctxt.fillStyle = "hsl("+(hue/360.0+0.001)+",0.3,0.65)";
+ ctxt.fillRect(xx, bump + ty*blockHeight, eventWidth, blockHeight);
+ /*}*/
+ highest[ty] = xx+eventWidth;
+ }
+ }
+
+ // binding loops
+ ctxt.strokeStyle = "orange";
+ ctxt.lineWidth = 2;
+ var radius = 1;
+ for (var ii = 0; ii < qmlProfilerModelProxy.count(); ++ii) {
+ if (qmlProfilerModelProxy.getBindingLoopDest(ii) >= 0) {
+ var xcenter = Math.round(qmlProfilerModelProxy.getStartTime(ii) +
+ qmlProfilerModelProxy.getDuration(ii) -
+ qmlProfilerModelProxy.traceStartTime()) * spacing;
+ var ycenter = Math.round(bump + qmlProfilerModelProxy.getType(ii) *
+ blockHeight + blockHeight/2);
+ ctxt.arc(xcenter, ycenter, radius, 0, 2*Math.PI, true);
+ ctxt.stroke();
+ }
+ }
+}
+
+function drawTimeBar(canvas, ctxt, region)
+{
+ if (!qmlProfilerModelProxy)
+ return;
+
+ var width = canvas.width;
+ var height = 10;
+ var startTime = qmlProfilerModelProxy.traceStartTime();
+ var endTime = qmlProfilerModelProxy.traceEndTime();
+
+ var totalTime = qmlProfilerModelProxy.traceDuration();
+ var spacing = width / totalTime;
+
+ var initialBlockLength = 120;
+ var timePerBlock = Math.pow(2, Math.floor( Math.log( totalTime / width *
+ initialBlockLength ) / Math.LN2 ) );
+ var pixelsPerBlock = timePerBlock * spacing;
+ var pixelsPerSection = pixelsPerBlock / 5;
+ var blockCount = width / pixelsPerBlock;
+
+ var realStartTime = Math.floor(startTime/timePerBlock) * timePerBlock;
+ var realStartPos = (startTime-realStartTime) * spacing;
+
+ var timePerPixel = timePerBlock/pixelsPerBlock;
+
+ ctxt.fillStyle = "#000000";
+ ctxt.font = "6px sans-serif";
+
+ ctxt.fillStyle = "#cccccc";
+ ctxt.fillRect(0, 0, width, height);
+ for (var ii = 0; ii < blockCount+1; ii++) {
+ var x = Math.floor(ii*pixelsPerBlock - realStartPos);
+
+ // block boundary
+ ctxt.strokeStyle = "#525252";
+ ctxt.beginPath();
+ ctxt.moveTo(x, height/2);
+ ctxt.lineTo(x, height);
+ ctxt.stroke();
+
+ // block time label
+ ctxt.fillStyle = "#000000";
+ var timeString = prettyPrintTime((ii+0.5)*timePerBlock + realStartTime);
+ ctxt.textAlign = "center";
+ ctxt.fillText(timeString, x + pixelsPerBlock/2, height/2 + 3);
+ }
+
+ ctxt.fillStyle = "#808080";
+ ctxt.fillRect(0, height-1, width, 1);
+}
+
+function prettyPrintTime( t )
+{
+ if (t <= 0) return "0";
+ if (t<1000) return t+" ns";
+ t = t/1000;
+ if (t<1000) return t+" μs";
+ t = Math.floor(t/100)/10;
+ if (t<1000) return t+" ms";
+ t = Math.floor(t)/1000;
+ if (t<60) return t+" s";
+ var m = Math.floor(t/60);
+ t = Math.floor(t - m*60);
+ return m+"m"+t+"s";
+}
+
+function plot(canvas, ctxt, region)
+{
+ drawGraph(canvas, ctxt, region);
+ drawData(canvas, ctxt, region);
+ drawTimeBar(canvas, ctxt, region);
+
+}
diff --git a/plugins/qmlprofiler/qml/Overview.qml b/plugins/qmlprofiler/qml/Overview.qml
new file mode 100644
index 0000000000..d90e1e6c7d
--- /dev/null
+++ b/plugins/qmlprofiler/qml/Overview.qml
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+import QtQuick 1.0
+import Monitor 1.0
+import "Overview.js" as Plotter
+
+Canvas2D {
+ id: canvas
+
+ // ***** properties
+ height: 50
+ property bool dataReady: false
+ property variant startTime : 0
+ property variant endTime : 0
+
+ // ***** functions
+ function clearDisplay()
+ {
+ dataReady = false;
+ requestRedraw();
+ }
+
+ function updateRange() {
+ var newStartTime = Math.round(rangeMover.x * qmlProfilerModelProxy.traceDuration() / width) + qmlProfilerModelProxy.traceStartTime();
+ var newEndTime = Math.round((rangeMover.x + rangeMover.width) * qmlProfilerModelProxy.traceDuration() / width) + qmlProfilerModelProxy.traceStartTime();
+ if (startTime !== newStartTime || endTime !== newEndTime) {
+ zoomControl.setRange(newStartTime, newEndTime);
+ }
+
+ }
+
+ // ***** connections to external objects
+ Connections {
+ target: zoomControl
+ onRangeChanged: {
+ if (qmlProfilerModelProxy) {
+ startTime = zoomControl.startTime();
+ endTime = zoomControl.endTime();
+ var newRangeX = (startTime - qmlProfilerModelProxy.traceStartTime()) * width / qmlProfilerModelProxy.traceDuration();
+ if (rangeMover.x !== newRangeX)
+ rangeMover.x = newRangeX;
+ var newWidth = (endTime-startTime) * width / qmlProfilerModelProxy.traceDuration();
+ if (rangeMover.width !== newWidth)
+ rangeMover.width = newWidth;
+ }
+ }
+ }
+
+ Connections {
+ target: qmlProfilerModelProxy
+ onDataAvailable: {
+ dataReady = true;
+ requestRedraw();
+ }
+ }
+
+
+ // ***** slots
+ onDrawRegion: {
+ Plotter.qmlProfilerModelProxy = qmlProfilerModelProxy;
+ if (dataReady) {
+ Plotter.plot(canvas, ctxt, region);
+ } else {
+ Plotter.drawGraph(canvas, ctxt, region) //just draw the background
+ }
+ }
+
+ // ***** child items
+ MouseArea {
+ anchors.fill: canvas
+ function jumpTo(posX) {
+ var newX = posX - rangeMover.width/2;
+ if (newX < 0)
+ newX = 0;
+ if (newX + rangeMover.width > canvas.width)
+ newX = canvas.width - rangeMover.width;
+ rangeMover.x = newX;
+ updateRange();
+ }
+
+ onPressed: {
+ jumpTo(mouse.x);
+ }
+ onMousePositionChanged: {
+ jumpTo(mouse.x);
+ }
+ }
+
+ RangeMover {
+ id: rangeMover
+ visible: dataReady
+ }
+
+ Rectangle {
+ height: 1
+ width: parent.width
+ color: "#858585"
+ }
+}
diff --git a/plugins/qmlprofiler/qml/RangeDetails.qml b/plugins/qmlprofiler/qml/RangeDetails.qml
new file mode 100644
index 0000000000..ef1a19234b
--- /dev/null
+++ b/plugins/qmlprofiler/qml/RangeDetails.qml
@@ -0,0 +1,222 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+import QtQuick 1.0
+import Monitor 1.0
+
+Item {
+ id: rangeDetails
+
+ property string duration
+ property string label
+ property string dialogTitle
+ property string file
+ property int line
+ property int column
+ property bool isBindingLoop
+
+ property bool locked: view.selectionLocked
+
+ width: col.width + 45
+ height: col.height + 30
+ z: 1
+ visible: false
+ x: 200
+ y: 25
+
+ property int yoffset: root.scrollY
+ onYoffsetChanged: {
+ y = relativey + yoffset
+ fitInView();
+ }
+ property int relativey : y - yoffset
+ onYChanged: relativey = y - yoffset
+
+ // keep inside view
+ Connections {
+ target: root
+ onWidthChanged: fitInView();
+ onCandidateHeightChanged: fitInView();
+ }
+
+ //property variant eventInfo
+
+ ListModel {
+ id: eventInfo
+ }
+
+ function showInfo(eventData) {
+ eventInfo.clear();
+ rangeDetails.dialogTitle = eventData[0]["title"];
+ for (var i = 1; i < eventData.length; i++) {
+ for (var k in eventData[i]) {
+ eventInfo.append({"key": k, "value":eventData[i][k]});
+ }
+ }
+ rangeDetails.visible = true;
+ }
+
+ function fitInView() {
+ // don't reposition if it does not fit
+ if (root.width < width || root.candidateHeight < height)
+ return;
+
+ if (x + width > root.width)
+ x = root.width - width;
+ if (x < 0)
+ x = 0;
+ if (y - yoffset + height > root.candidateHeight)
+ y = root.candidateHeight - height + yoffset;
+ if (y < yoffset)
+ y = yoffset;
+ }
+
+ // shadow
+ BorderImage {
+ property int px: 4
+ source: "dialog_shadow.png"
+
+ border {
+ left: px; top: px
+ right: px; bottom: px
+ }
+ width: parent.width + 2*px - 1
+ height: parent.height
+ x: -px + 1
+ y: px + 1
+ }
+
+ // title bar
+ Rectangle {
+ width: parent.width
+ height: 20
+ color: "#55a3b8"
+ radius: 5
+ border.width: 1
+ border.color: "#a0a0a0"
+ }
+ Item {
+ width: parent.width+1
+ height: 11
+ y: 10
+ clip: true
+ Rectangle {
+ width: parent.width-1
+ height: 15
+ y: -5
+ color: "#55a3b8"
+ border.width: 1
+ border.color: "#a0a0a0"
+ }
+ }
+
+ //title
+ Text {
+ id: typeTitle
+ text: " "+rangeDetails.dialogTitle
+ font.bold: true
+ height: 18
+ y: 2
+ verticalAlignment: Text.AlignVCenter
+ width: parent.width
+ color: "white"
+ }
+
+ // Details area
+ Rectangle {
+ color: "white"
+ width: parent.width
+ height: col.height + 10
+ y: 20
+ border.width: 1
+ border.color: "#a0a0a0"
+
+ //details
+ Column {
+ id: col
+ x: 10
+ y: 5
+
+ Repeater {
+ model: eventInfo
+ Detail {
+ label: key
+ content: value
+ }
+ }
+ }
+ }
+
+ MouseArea {
+ width: col.width + 45
+ height: col.height + 30
+ drag.target: parent
+ drag.minimumX: 0
+ drag.maximumX: root.width - parent.width
+ drag.minimumY: yoffset
+ drag.maximumY: root.candidateHeight - parent.height + yoffset
+ onClicked: {
+ root.gotoSourceLocation(file, line, column);
+ root.recenterOnItem(view.selectedItem);
+ }
+ }
+
+ Image {
+ id: lockIcon
+ source: locked?"lock_closed.png" : "lock_open.png"
+ anchors.top: closeIcon.top
+ anchors.right: closeIcon.left
+ anchors.rightMargin: 4
+ width: 8
+ height: 12
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ root.selectionLocked = !root.selectionLocked;
+ }
+ }
+ }
+
+
+ Text {
+ id: closeIcon
+ x: col.width + 30
+ y: 4
+ text:"X"
+ color: "white"
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ root.hideRangeDetails();
+ view.selectedItem = -1;
+ }
+ }
+ }
+
+}
diff --git a/plugins/qmlprofiler/qml/RangeMover.qml b/plugins/qmlprofiler/qml/RangeMover.qml
new file mode 100644
index 0000000000..18226d8d25
--- /dev/null
+++ b/plugins/qmlprofiler/qml/RangeMover.qml
@@ -0,0 +1,203 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+import QtQuick 1.0
+
+Rectangle {
+ id: rangeMover
+
+
+ property color rangeColor:"#444a64b8"
+ property color borderColor:"#cc4a64b8"
+ property color dragMarkerColor: "#4a64b8"
+ width: 20
+ height: 50
+
+ color: rangeColor
+
+ property bool dragStarted: false
+ onXChanged: {
+ if (dragStarted) canvas.updateRange()
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ drag.target: rangeMover
+ drag.axis: "XAxis"
+ drag.minimumX: 0
+ drag.maximumX: canvas.width - rangeMover.width
+ onPressed: {
+ parent.dragStarted = true;
+ }
+ onReleased: {
+ parent.dragStarted = false;
+ }
+ }
+
+ Rectangle {
+ id: leftRange
+
+ // used for dragging the borders
+ property real initialX: 0
+ property real initialWidth: 0
+
+ x: 0
+ height: parent.height
+ width: 1
+ color: borderColor
+
+ Rectangle {
+ id: leftBorderHandle
+ height: parent.height
+ x: -width
+ width: 7
+ color: "#869cd1"
+ visible: false
+ Image {
+ source: "range_handle.png"
+ x: 2
+ width: 4
+ height: 9
+ fillMode: Image.Tile
+ y: rangeMover.height / 2 - 4
+ }
+ }
+
+ states: State {
+ name: "highlighted"
+ PropertyChanges {
+ target: leftBorderHandle
+ visible: true
+ }
+ }
+
+ onXChanged: {
+ if (x !== 0) {
+ rangeMover.width = initialWidth - x;
+ rangeMover.x = initialX + x;
+ x = 0;
+ canvas.updateRange();
+ }
+ }
+
+ MouseArea {
+ x: -10
+ width: 13
+ y: 0
+ height: parent.height
+
+ drag.target: leftRange
+ drag.axis: "XAxis"
+ drag.minimumX: -parent.initialX
+ drag.maximumX: parent.initialWidth - 2
+
+ hoverEnabled: true
+
+ onEntered: {
+ parent.state = "highlighted";
+ }
+ onExited: {
+ if (!pressed) parent.state = "";
+ }
+ onReleased: {
+ if (!containsMouse) parent.state = "";
+ }
+ onPressed: {
+ parent.initialX = rangeMover.x;
+ parent.initialWidth = rangeMover.width;
+ }
+ }
+ }
+
+ Rectangle {
+ id: rightRange
+
+ x: rangeMover.width
+ height: parent.height
+ width: 1
+ color: borderColor
+
+ Rectangle {
+ id: rightBorderHandle
+ height: parent.height
+ x: 1
+ width: 7
+ color: "#869cd1"
+ visible: false
+ Image {
+ source: "range_handle.png"
+ x: 2
+ width: 4
+ height: 9
+ fillMode: Image.Tile
+ y: rangeMover.height / 2 - 4
+ }
+ }
+
+ states: State {
+ name: "highlighted"
+ PropertyChanges {
+ target: rightBorderHandle
+ visible: true
+ }
+ }
+
+ onXChanged: {
+ if (x!=rangeMover.width) {
+ rangeMover.width = x;
+ canvas.updateRange();
+ }
+ }
+
+ MouseArea {
+ x: -3
+ width: 13
+ y: 0
+ height: parent.height
+
+ drag.target: rightRange
+ drag.axis: "XAxis"
+ drag.minimumX: 1
+ drag.maximumX: canvas.width - rangeMover.x
+
+ hoverEnabled: true
+
+ onEntered: {
+ parent.state = "highlighted";
+ }
+ onExited: {
+ if (!pressed) parent.state = "";
+ }
+ onReleased: {
+ if (!containsMouse) parent.state = "";
+ }
+ }
+ }
+
+}
diff --git a/plugins/qmlprofiler/qml/SelectionRange.qml b/plugins/qmlprofiler/qml/SelectionRange.qml
new file mode 100644
index 0000000000..b302de614a
--- /dev/null
+++ b/plugins/qmlprofiler/qml/SelectionRange.qml
@@ -0,0 +1,297 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+import QtQuick 1.0
+
+Rectangle {
+ id: selectionRange
+
+ width: 1
+ color: "transparent"
+
+ property bool ready: visible && creationState === 3
+
+ property color rangeColor:"#444a64b8"
+ property color pressedColor:"#664a64b8"
+ property color borderColor:"#aa4a64b8"
+ property color dragMarkerColor: "#4a64b8"
+ property color singleLineColor: "#4a64b8"
+
+ property string startTimeString: detailedPrintTime(startTime)
+ property string endTimeString: detailedPrintTime(startTime+duration)
+ property string durationString: detailedPrintTime(duration)
+
+ property variant startTime: x * selectionRange.viewTimePerPixel + qmlProfilerModelProxy.traceStartTime()
+ property variant duration: width * selectionRange.viewTimePerPixel
+ property variant viewTimePerPixel: 1
+ property variant creationState : 0
+
+ property variant x1
+ property variant x2
+ property variant x3: Math.min(x1, x2)
+ property variant x4: Math.max(x1, x2)
+
+ property bool isDragging: false
+
+ Connections {
+ target: zoomControl
+ onRangeChanged: {
+ var oldTimePerPixel = selectionRange.viewTimePerPixel;
+ selectionRange.viewTimePerPixel = Math.abs(zoomControl.endTime() - zoomControl.startTime()) / flick.width;
+ if (creationState === 3 && oldTimePerPixel != selectionRange.viewTimePerPixel) {
+ selectionRange.x = x * oldTimePerPixel / selectionRange.viewTimePerPixel;
+ selectionRange.width = width * oldTimePerPixel / selectionRange.viewTimePerPixel;
+ }
+ }
+ }
+
+ onCreationStateChanged: {
+ switch (creationState) {
+ case 0: color = "transparent"; break;
+ case 1: color = singleLineColor; break;
+ default: color = rangeColor; break;
+ }
+ }
+
+ onIsDraggingChanged: {
+ if (isDragging)
+ color = pressedColor;
+ else
+ color = rangeColor;
+ }
+
+ function reset(setVisible) {
+ width = 1;
+ creationState = 0;
+ visible = setVisible;
+ }
+
+ function setPos(pos) {
+ switch (creationState) {
+ case 1: {
+ width = 1;
+ x1 = pos;
+ x2 = pos;
+ x = pos;
+ break;
+ }
+ case 2: {
+ x2 = pos;
+ x = x3;
+ width = x4-x3;
+ break;
+ }
+ default: return;
+ }
+ }
+
+
+ function detailedPrintTime( t )
+ {
+ if (t <= 0) return "0";
+ if (t<1000) return t+" ns";
+ t = Math.floor(t/1000);
+ if (t<1000) return t+" μs";
+ if (t<1e6) return (t/1000) + " ms";
+ return (t/1e6) + " s";
+ }
+
+ // creation control
+ function releasedOnCreation() {
+ if (selectionRange.creationState === 2) {
+ flick.interactive = true;
+ selectionRange.creationState = 3;
+ selectionRangeControl.enabled = false;
+ }
+ }
+
+ function pressedOnCreation() {
+ if (selectionRange.creationState === 1) {
+ flick.interactive = false;
+ selectionRange.setPos(selectionRangeControl.mouseX + flick.contentX);
+ selectionRange.creationState = 2;
+ }
+ }
+
+ function movedOnCreation() {
+ if (selectionRange.creationState === 0) {
+ selectionRange.creationState = 1;
+ }
+
+ if (!root.eventCount)
+ return;
+
+ if (!selectionRangeControl.pressed && selectionRange.creationState==3)
+ return;
+
+ if (selectionRangeControl.pressed) {
+ selectionRange.setPos(selectionRangeControl.mouseX + flick.contentX);
+ } else {
+ selectionRange.setPos(selectionRangeControl.mouseX + flick.contentX);
+ }
+ }
+
+ Rectangle {
+ id: leftBorder
+
+ visible: selectionRange.creationState === 3
+
+ // used for dragging the borders
+ property real initialX: 0
+ property real initialWidth: 0
+
+ x: 0
+ height: parent.height
+ width: 1
+ color: borderColor
+
+ Rectangle {
+ id: leftBorderHandle
+ height: parent.height
+ x: -width
+ width: 9
+ color: "#869cd1"
+ visible: false
+ Image {
+ source: "range_handle.png"
+ x: 4
+ width: 4
+ height: 63
+ fillMode: Image.Tile
+ y: root.scrollY + root.candidateHeight / 2 - 32
+ }
+ }
+
+ states: State {
+ name: "highlighted"
+ PropertyChanges {
+ target: leftBorderHandle
+ visible: true
+ }
+ }
+
+ onXChanged: if (x != 0) {
+ selectionRange.width = initialWidth - x;
+ selectionRange.x = initialX + x;
+ x = 0;
+ }
+
+ MouseArea {
+ x: -12
+ width: 15
+ y: 0
+ height: parent.height
+
+ drag.target: leftBorder
+ drag.axis: "XAxis"
+ drag.minimumX: -parent.initialX
+ drag.maximumX: parent.initialWidth - 2
+
+ hoverEnabled: true
+
+ onEntered: parent.state = "highlighted"
+ onExited: {
+ if (!pressed) parent.state = "";
+ }
+ onReleased: {
+ if (!containsMouse) parent.state = "" ;
+ }
+ onPressed: {
+ parent.initialX = selectionRange.x;
+ parent.initialWidth = selectionRange.width;
+ }
+ }
+ }
+
+ Rectangle {
+ id: rightBorder
+
+ visible: selectionRange.creationState === 3
+
+ x: selectionRange.width
+ height: parent.height
+ width: 1
+ color: borderColor
+
+ Rectangle {
+ id: rightBorderHandle
+ height: parent.height
+ x: 1
+ width: 9
+ color: "#869cd1"
+ visible: false
+ Image {
+ source: "range_handle.png"
+ x: 2
+ width: 4
+ height: 63
+ fillMode: Image.Tile
+ y: root.scrollY + root.candidateHeight / 2 - 32
+ }
+ }
+
+ states: State {
+ name: "highlighted"
+ PropertyChanges {
+ target: rightBorderHandle
+ visible: true
+ }
+ }
+
+ onXChanged: {
+ if (x != selectionRange.width) {
+ selectionRange.width = x;
+ }
+ }
+
+ MouseArea {
+ x: -3
+ width: 15
+ y: 0
+ height: parent.height
+
+ drag.target: rightBorder
+ drag.axis: "XAxis"
+ drag.minimumX: 1
+ drag.maximumX: flick.contentWidth - selectionRange.x
+
+ hoverEnabled: true
+
+ onEntered: {
+ parent.state = "highlighted";
+ }
+ onExited: {
+ if (!pressed) parent.state = "";
+ }
+ onReleased: {
+ if (!containsMouse) parent.state = "";
+ }
+ }
+ }
+}
diff --git a/plugins/qmlprofiler/qml/SelectionRangeDetails.qml b/plugins/qmlprofiler/qml/SelectionRangeDetails.qml
new file mode 100644
index 0000000000..5af24af14d
--- /dev/null
+++ b/plugins/qmlprofiler/qml/SelectionRangeDetails.qml
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+import QtQuick 1.0
+import Monitor 1.0
+
+Item {
+ id: selectionRangeDetails
+
+ property string startTime
+ property string endTime
+ property string duration
+ property bool showDuration
+
+ width: 170
+ height: col.height + 30
+ z: 1
+ visible: false
+ x: 200
+ y: 125
+
+ property int yoffset: root.scrollY
+ onYoffsetChanged: {
+ y = relativey + yoffset
+ fitInView();
+ }
+ property int relativey : y - yoffset
+ onYChanged: relativey = y - yoffset
+
+ // keep inside view
+ Connections {
+ target: root
+ onWidthChanged: fitInView();
+ onCandidateHeightChanged: fitInView();
+ }
+
+ function fitInView() {
+ // don't reposition if it does not fit
+ if (root.width < width || root.candidateHeight < height)
+ return;
+
+ if (x + width > root.width)
+ x = root.width - width;
+ if (x < 0)
+ x = 0;
+ if (y + height - yoffset > root.candidateHeight)
+ y = root.candidateHeight - height + yoffset;
+ if (y < yoffset)
+ y = yoffset;
+ }
+
+ // shadow
+ BorderImage {
+ property int px: 4
+ source: "dialog_shadow.png"
+
+ border {
+ left: px; top: px
+ right: px; bottom: px
+ }
+ width: parent.width + 2*px - 1
+ height: parent.height
+ x: -px + 1
+ y: px + 1
+ }
+
+ // title bar
+ Rectangle {
+ width: parent.width
+ height: 20
+ color: "#4a64b8"
+ radius: 5
+ border.width: 1
+ border.color: "#a0a0a0"
+ }
+ Item {
+ width: parent.width+1
+ height: 11
+ y: 10
+ clip: true
+ Rectangle {
+ width: parent.width-1
+ height: 15
+ y: -5
+ color: "#4a64b8"
+ border.width: 1
+ border.color: "#a0a0a0"
+ }
+ }
+
+ //title
+ Text {
+ id: typeTitle
+ text: " "+qsTr("Selection")
+ font.bold: true
+ height: 18
+ y: 2
+ verticalAlignment: Text.AlignVCenter
+ width: parent.width
+ color: "white"
+ }
+
+ // Details area
+ Rectangle {
+ color: "white"
+ width: parent.width
+ height: col.height + 10
+ y: 20
+ border.width: 1
+ border.color: "#a0a0a0"
+ Column {
+ id: col
+ x: 10
+ y: 5
+ Detail {
+ label: qsTr("Start")
+ content: selectionRangeDetails.startTime
+ }
+ Detail {
+ label: qsTr("End")
+ visible: selectionRangeDetails.showDuration
+ content: selectionRangeDetails.endTime
+ }
+ Detail {
+ label: qsTr("Duration")
+ visible: selectionRangeDetails.showDuration
+ content: selectionRangeDetails.duration
+ }
+ }
+ }
+
+ MouseArea {
+ width: col.width + 45
+ height: col.height + 30
+ drag.target: parent
+ drag.minimumX: 0
+ drag.maximumX: root.width - parent.width
+ drag.minimumY: yoffset
+ drag.maximumY: root.candidateHeight - parent.height + yoffset
+ onClicked: {
+ if ((selectionRange.x < flick.contentX) ^ (selectionRange.x+selectionRange.width > flick.contentX + flick.width)) {
+ root.recenter(selectionRange.startTime + selectionRange.duration/2);
+ }
+ }
+ }
+
+ Text {
+ id: closeIcon
+ x: selectionRangeDetails.width - 14
+ y: 4
+ text:"X"
+ color: "white"
+ MouseArea {
+ anchors.fill: parent
+ anchors.leftMargin: -8
+ onClicked: {
+ root.selectionRangeMode = false;
+ root.updateRangeButton();
+ }
+ }
+ }
+
+}
diff --git a/plugins/qmlprofiler/qml/TimeDisplay.qml b/plugins/qmlprofiler/qml/TimeDisplay.qml
new file mode 100644
index 0000000000..2f5a8bfaaf
--- /dev/null
+++ b/plugins/qmlprofiler/qml/TimeDisplay.qml
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+import QtQuick 1.0
+import Monitor 1.0
+
+Canvas2D {
+ id: timeDisplay
+
+ property variant startTime : 0
+ property variant endTime : 0
+ property variant timePerPixel: 0
+
+
+ Component.onCompleted: {
+ requestRedraw();
+ }
+ onWidthChanged: {
+ requestRedraw();
+ }
+ onHeightChanged: {
+ requestRedraw();
+ }
+
+ Connections {
+ target: zoomControl
+ onRangeChanged: {
+ startTime = zoomControl.startTime();
+ endTime = zoomControl.endTime();
+ requestRedraw();
+ }
+ }
+
+ onDrawRegion: {
+ ctxt.fillStyle = "white";
+ ctxt.fillRect(0, 0, width, height);
+
+ var totalTime = endTime - startTime;
+ var spacing = width / totalTime;
+
+ var initialBlockLength = 120;
+ var timePerBlock = Math.pow(2, Math.floor( Math.log( totalTime / width * initialBlockLength ) / Math.LN2 ) );
+ var pixelsPerBlock = timePerBlock * spacing;
+ var pixelsPerSection = pixelsPerBlock / 5;
+ var blockCount = width / pixelsPerBlock;
+
+ var realStartTime = Math.floor(startTime/timePerBlock) * timePerBlock;
+ var realStartPos = (startTime-realStartTime) * spacing;
+
+ timePerPixel = timePerBlock/pixelsPerBlock;
+
+ var initialColor = Math.floor(realStartTime/timePerBlock) % 2;
+
+ ctxt.fillStyle = "#000000";
+ ctxt.font = "8px sans-serif";
+ for (var ii = 0; ii < blockCount+1; ii++) {
+ var x = Math.floor(ii*pixelsPerBlock - realStartPos);
+
+ ctxt.fillStyle = (ii+initialColor)%2 ? "#E6E6E6":"white";
+ ctxt.fillRect(x, 0, pixelsPerBlock, height);
+
+ ctxt.strokeStyle = "#B0B0B0";
+ ctxt.beginPath();
+ ctxt.moveTo(x, 0);
+ ctxt.lineTo(x, height);
+ ctxt.stroke();
+
+ ctxt.fillStyle = "#000000";
+ ctxt.fillText(prettyPrintTime(ii*timePerBlock + realStartTime), x + 5, height/2 + 5);
+ }
+
+ ctxt.strokeStyle = "#525252";
+ ctxt.beginPath();
+ ctxt.moveTo(0, height-1);
+ ctxt.lineTo(width, height-1);
+ ctxt.stroke();
+
+ // gradient borders
+ var gradientDark = "rgba(0, 0, 0, 0.53125)";
+ var gradientClear = "rgba(0, 0, 0, 0)";
+ var grad = ctxt.createLinearGradient(0, 0, 0, 6);
+ grad.addColorStop(0,gradientDark);
+ grad.addColorStop(1,gradientClear);
+ ctxt.fillStyle = grad;
+ ctxt.fillRect(0, 0, width, 6);
+
+ grad = ctxt.createLinearGradient(0, 0, 6, 0);
+ grad.addColorStop(0,gradientDark);
+ grad.addColorStop(1,gradientClear);
+ ctxt.fillStyle = grad;
+ ctxt.fillRect(0, 0, 6, height);
+
+ grad = ctxt.createLinearGradient(width, 0, width-6, 0);
+ grad.addColorStop(0,gradientDark);
+ grad.addColorStop(1,gradientClear);
+ ctxt.fillStyle = grad;
+ ctxt.fillRect(width-6, 0, 6, height);
+ }
+
+ function prettyPrintTime( t )
+ {
+ if (t <= 0) return "0";
+ if (t<1000) return t+" ns";
+ t = t/1000;
+ if (t<1000) return t+" μs";
+ t = Math.floor(t/100)/10;
+ if (t<1000) return t+" ms";
+ t = Math.floor(t)/1000;
+ if (t<60) return t+" s";
+ var m = Math.floor(t/60);
+ t = Math.floor(t - m*60);
+ return m+"m"+t+"s";
+ }
+}
diff --git a/plugins/qmlprofiler/qml/TimeMarks.qml b/plugins/qmlprofiler/qml/TimeMarks.qml
new file mode 100644
index 0000000000..dff304752e
--- /dev/null
+++ b/plugins/qmlprofiler/qml/TimeMarks.qml
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+import QtQuick 1.0
+import Monitor 1.0
+
+Canvas2D {
+ id: timeDisplay
+
+ property variant startTime
+ property variant endTime
+ property variant timePerPixel
+
+ Component.onCompleted: {
+ requestRedraw();
+ }
+
+ onWidthChanged: {
+ requestRedraw();
+ }
+ onHeightChanged: {
+ requestRedraw();
+ }
+
+ Connections {
+ target: labels
+ onHeightChanged: { requestRedraw(); }
+ }
+
+ onDrawRegion: {
+ drawBackgroundBars( ctxt, region );
+
+ var totalTime = endTime - startTime;
+ var spacing = width / totalTime;
+
+ var initialBlockLength = 120;
+ var timePerBlock = Math.pow(2, Math.floor( Math.log( totalTime / width * initialBlockLength ) / Math.LN2 ) );
+ var pixelsPerBlock = timePerBlock * spacing;
+ var pixelsPerSection = pixelsPerBlock / 5;
+ var blockCount = width / pixelsPerBlock;
+
+ var realStartTime = Math.floor(startTime/timePerBlock) * timePerBlock;
+ var realStartPos = (startTime-realStartTime) * spacing;
+
+ timePerPixel = timePerBlock/pixelsPerBlock;
+
+
+ ctxt.fillStyle = "#000000";
+ ctxt.font = "8px sans-serif";
+ for (var ii = 0; ii < blockCount+1; ii++) {
+ var x = Math.floor(ii*pixelsPerBlock - realStartPos);
+ ctxt.strokeStyle = "#B0B0B0";
+ ctxt.beginPath();
+ ctxt.moveTo(x, 0);
+ ctxt.lineTo(x, height);
+ ctxt.stroke();
+
+ ctxt.strokeStyle = "#CCCCCC";
+ for (var jj=1; jj < 5; jj++) {
+ var xx = Math.floor(ii*pixelsPerBlock + jj*pixelsPerSection - realStartPos);
+ ctxt.beginPath();
+ ctxt.moveTo(xx, 0);
+ ctxt.lineTo(xx, height);
+ ctxt.stroke();
+ }
+ }
+
+
+ // gray off out-of-bounds areas
+ var rectWidth;
+ if (startTime < qmlProfilerModelProxy.traceStartTime()) {
+ ctxt.fillStyle = "rgba(127,127,127,0.2)";
+ rectWidth = (qmlProfilerModelProxy.traceStartTime() - startTime) * spacing;
+ ctxt.fillRect(0, 0, rectWidth, height);
+ }
+ if (endTime > qmlProfilerModelProxy.traceEndTime()) {
+ ctxt.fillStyle = "rgba(127,127,127,0.2)";
+ var rectX = (qmlProfilerModelProxy.traceEndTime() - startTime) * spacing;
+ rectWidth = (endTime - qmlProfilerModelProxy.traceEndTime()) * spacing;
+ ctxt.fillRect(rectX, 0, rectWidth, height);
+ }
+
+ }
+
+ function updateMarks(start, end) {
+ if (startTime !== start || endTime !== end) {
+ startTime = start;
+ endTime = end;
+ requestRedraw();
+ }
+ }
+
+ function drawBackgroundBars( ctxt, region ) {
+ // TODO: redraw when amount of data changes
+ var colorIndex = true;
+ // row background
+ for (var y=0; y < labels.height; y+= root.singleRowHeight) {
+ ctxt.fillStyle = colorIndex ? "#f0f0f0" : "white";
+ ctxt.strokeStyle = colorIndex ? "#f0f0f0" : "white";
+ ctxt.fillRect(0, y, width, root.singleRowHeight);
+ colorIndex = !colorIndex;
+ }
+
+ // separators
+ var cumulatedHeight = 0;
+ for (var i=0; i<labels.rowCount; i++) {
+ cumulatedHeight += root.singleRowHeight * qmlProfilerModelProxy.categoryDepth(i);
+
+ ctxt.strokeStyle = "#B0B0B0";
+ ctxt.beginPath();
+ ctxt.moveTo(0, cumulatedHeight);
+ ctxt.lineTo(width, cumulatedHeight);
+ ctxt.stroke();
+ }
+
+ // bottom
+ ctxt.fillStyle = "#f5f5f5";
+ ctxt.fillRect(0, labels.height, width, height - labels.height);
+ }
+}
diff --git a/plugins/qmlprofiler/qml/arrow_down.png b/plugins/qmlprofiler/qml/arrow_down.png
new file mode 100644
index 0000000000..94300b5944
--- /dev/null
+++ b/plugins/qmlprofiler/qml/arrow_down.png
Binary files differ
diff --git a/plugins/qmlprofiler/qml/arrow_right.png b/plugins/qmlprofiler/qml/arrow_right.png
new file mode 100644
index 0000000000..e124813da3
--- /dev/null
+++ b/plugins/qmlprofiler/qml/arrow_right.png
Binary files differ
diff --git a/plugins/qmlprofiler/qml/clean_pane_small.png b/plugins/qmlprofiler/qml/clean_pane_small.png
new file mode 100644
index 0000000000..583b567703
--- /dev/null
+++ b/plugins/qmlprofiler/qml/clean_pane_small.png
Binary files differ
diff --git a/plugins/qmlprofiler/qml/dialog_shadow.png b/plugins/qmlprofiler/qml/dialog_shadow.png
new file mode 100644
index 0000000000..2d697325c3
--- /dev/null
+++ b/plugins/qmlprofiler/qml/dialog_shadow.png
Binary files differ
diff --git a/plugins/qmlprofiler/qml/ico_next.png b/plugins/qmlprofiler/qml/ico_next.png
new file mode 100644
index 0000000000..dad2f241c2
--- /dev/null
+++ b/plugins/qmlprofiler/qml/ico_next.png
Binary files differ
diff --git a/plugins/qmlprofiler/qml/ico_prev.png b/plugins/qmlprofiler/qml/ico_prev.png
new file mode 100644
index 0000000000..7794d3d291
--- /dev/null
+++ b/plugins/qmlprofiler/qml/ico_prev.png
Binary files differ
diff --git a/plugins/qmlprofiler/qml/ico_rangeselected.png b/plugins/qmlprofiler/qml/ico_rangeselected.png
new file mode 100644
index 0000000000..c6a422461a
--- /dev/null
+++ b/plugins/qmlprofiler/qml/ico_rangeselected.png
Binary files differ
diff --git a/plugins/qmlprofiler/qml/ico_rangeselection.png b/plugins/qmlprofiler/qml/ico_rangeselection.png
new file mode 100644
index 0000000000..f303a21c8c
--- /dev/null
+++ b/plugins/qmlprofiler/qml/ico_rangeselection.png
Binary files differ
diff --git a/plugins/qmlprofiler/qml/ico_selectionmode.png b/plugins/qmlprofiler/qml/ico_selectionmode.png
new file mode 100644
index 0000000000..ac23d47b26
--- /dev/null
+++ b/plugins/qmlprofiler/qml/ico_selectionmode.png
Binary files differ
diff --git a/plugins/qmlprofiler/qml/ico_zoom.png b/plugins/qmlprofiler/qml/ico_zoom.png
new file mode 100644
index 0000000000..9235535eaa
--- /dev/null
+++ b/plugins/qmlprofiler/qml/ico_zoom.png
Binary files differ
diff --git a/plugins/qmlprofiler/qml/lock_closed.png b/plugins/qmlprofiler/qml/lock_closed.png
new file mode 100644
index 0000000000..e6f86fa753
--- /dev/null
+++ b/plugins/qmlprofiler/qml/lock_closed.png
Binary files differ
diff --git a/plugins/qmlprofiler/qml/lock_open.png b/plugins/qmlprofiler/qml/lock_open.png
new file mode 100644
index 0000000000..ab6c820dfb
--- /dev/null
+++ b/plugins/qmlprofiler/qml/lock_open.png
Binary files differ
diff --git a/plugins/qmlprofiler/qml/qmlprofiler.qrc b/plugins/qmlprofiler/qml/qmlprofiler.qrc
new file mode 100644
index 0000000000..ab3f8598da
--- /dev/null
+++ b/plugins/qmlprofiler/qml/qmlprofiler.qrc
@@ -0,0 +1,30 @@
+<RCC>
+ <qresource prefix="/qmlprofiler">
+ <file>Detail.qml</file>
+ <file>Label.qml</file>
+ <file>MainView.qml</file>
+ <file>RangeDetails.qml</file>
+ <file>RangeMover.qml</file>
+ <file>TimeDisplay.qml</file>
+ <file>clean_pane_small.png</file>
+ <file>recordOff.png</file>
+ <file>recordOn.png</file>
+ <file>lock_closed.png</file>
+ <file>lock_open.png</file>
+ <file>TimeMarks.qml</file>
+ <file>Overview.qml</file>
+ <file>Overview.js</file>
+ <file>SelectionRange.qml</file>
+ <file>SelectionRangeDetails.qml</file>
+ <file>arrow_down.png</file>
+ <file>arrow_right.png</file>
+ <file>dialog_shadow.png</file>
+ <file>range_handle.png</file>
+ <file>ico_selectionmode.png</file>
+ <file>ico_zoom.png</file>
+ <file>ico_prev.png</file>
+ <file>ico_next.png</file>
+ <file>ico_rangeselection.png</file>
+ <file>ico_rangeselected.png</file>
+ </qresource>
+</RCC>
diff --git a/plugins/qmlprofiler/qml/range_handle.png b/plugins/qmlprofiler/qml/range_handle.png
new file mode 100644
index 0000000000..f44fb3337f
--- /dev/null
+++ b/plugins/qmlprofiler/qml/range_handle.png
Binary files differ
diff --git a/plugins/qmlprofiler/qml/recordOff.png b/plugins/qmlprofiler/qml/recordOff.png
new file mode 100644
index 0000000000..d50c0183c0
--- /dev/null
+++ b/plugins/qmlprofiler/qml/recordOff.png
Binary files differ
diff --git a/plugins/qmlprofiler/qml/recordOn.png b/plugins/qmlprofiler/qml/recordOn.png
new file mode 100644
index 0000000000..ed5e8912c3
--- /dev/null
+++ b/plugins/qmlprofiler/qml/recordOn.png
Binary files differ
diff --git a/plugins/qmlprofiler/qmlprofiler.pro b/plugins/qmlprofiler/qmlprofiler.pro
new file mode 100644
index 0000000000..f5af14afab
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofiler.pro
@@ -0,0 +1,74 @@
+DEFINES += QMLPROFILER_LIBRARY
+
+QT += network script declarative
+
+include(../../qtcreatorplugin.pri)
+include(canvas/canvas.pri)
+
+SOURCES += \
+ qmlprofilerplugin.cpp \
+ qmlprofilertool.cpp \
+ qmlprofilerengine.cpp \
+ qmlprofilerattachdialog.cpp \
+ localqmlprofilerrunner.cpp \
+ qmlprofilereventview.cpp \
+ qv8profilereventview.cpp \
+ qmlprofilerdetailsrewriter.cpp \
+ qmlprofilertraceview.cpp \
+ timelinerenderer.cpp \
+ qmlprofilerstatemanager.cpp \
+ qv8profilerdatamodel.cpp \
+ qmlprofilerclientmanager.cpp \
+ qmlprofilerviewmanager.cpp \
+ qmlprofilerstatewidget.cpp \
+ qmlprofilermodelmanager.cpp \
+ qmlprofilersimplemodel.cpp \
+ qmlprofilerprocessedmodel.cpp \
+ qmlprofilereventsmodelproxy.cpp \
+ qmlprofilertimelinemodelproxy.cpp \
+ qmlprofileroverviewmodelproxy.cpp \
+ qmlprofilertreeview.cpp \
+ qmlprofilertracefile.cpp
+
+HEADERS += \
+ qmlprofilerconstants.h \
+ qmlprofiler_global.h \
+ qmlprofilerplugin.h \
+ qmlprofilertool.h \
+ qmlprofilerengine.h \
+ qmlprofilerattachdialog.h \
+ abstractqmlprofilerrunner.h \
+ localqmlprofilerrunner.h \
+ qmlprofilereventview.h \
+ qv8profilereventview.h \
+ qmlprofilerdetailsrewriter.h \
+ qmlprofilertraceview.h \
+ timelinerenderer.h \
+ qmlprofilerstatemanager.h \
+ qv8profilerdatamodel.h \
+ qmlprofilerclientmanager.h \
+ qmlprofilerviewmanager.h \
+ qmlprofilerstatewidget.h \
+ qmlprofilermodelmanager.h \
+ qmlprofilersimplemodel.h \
+ qmlprofilerprocessedmodel.h \
+ qmlprofilereventsmodelproxy.h \
+ qmlprofilertimelinemodelproxy.h \
+ qmlprofileroverviewmodelproxy.h \
+ qmlprofilertreeview.h \
+ qmlprofilertracefile.h
+
+RESOURCES += \
+ qml/qmlprofiler.qrc
+
+OTHER_FILES += \
+ qml/Detail.qml \
+ qml/Label.qml \
+ qml/MainView.qml \
+ qml/RangeDetails.qml \
+ qml/RangeMover.qml \
+ qml/TimeDisplay.qml \
+ qml/TimeMarks.qml \
+ qml/SelectionRange.qml \
+ qml/SelectionRangeDetails.qml \
+ qml/Overview.qml
diff --git a/plugins/qmlprofiler/qmlprofiler.qbs b/plugins/qmlprofiler/qmlprofiler.qbs
new file mode 100644
index 0000000000..3ae2365f49
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofiler.qbs
@@ -0,0 +1,96 @@
+import qbs.base 1.0
+
+import "../QtcPlugin.qbs" as QtcPlugin
+
+QtcPlugin {
+ name: "QmlProfiler"
+
+ Depends { name: "Qt"; submodules: ["widgets", "network", "script", "declarative"] }
+ Depends { name: "Core" }
+ Depends { name: "AnalyzerBase" }
+ Depends { name: "Debugger" }
+ Depends { name: "QmlProjectManager" }
+ Depends { name: "Qt4ProjectManager" }
+ Depends { name: "RemoteLinux" }
+ Depends { name: "ProjectExplorer" }
+ Depends { name: "QtSupport" }
+ Depends { name: "TextEditor" }
+ Depends { name: "QmlDebug" }
+ Depends { name: "QmlJS" }
+ Depends { name: "QmlJSTools" }
+ Depends { name: "CPlusPlus" }
+
+ cpp.includePaths: base.concat("canvas")
+
+ files: [
+ "abstractqmlprofilerrunner.h",
+ "localqmlprofilerrunner.cpp",
+ "localqmlprofilerrunner.h",
+ "qmlprofiler_global.h",
+ "qmlprofilerattachdialog.cpp",
+ "qmlprofilerattachdialog.h",
+ "qmlprofilerclientmanager.cpp",
+ "qmlprofilerclientmanager.h",
+ "qmlprofilerconstants.h",
+ "qmlprofilerdetailsrewriter.cpp",
+ "qmlprofilerdetailsrewriter.h",
+ "qmlprofilerengine.cpp",
+ "qmlprofilerengine.h",
+ "qmlprofilereventsmodelproxy.cpp",
+ "qmlprofilereventsmodelproxy.h",
+ "qmlprofilereventview.cpp",
+ "qmlprofilereventview.h",
+ "qmlprofilermodelmanager.cpp",
+ "qmlprofilermodelmanager.h",
+ "qmlprofileroverviewmodelproxy.cpp",
+ "qmlprofileroverviewmodelproxy.h",
+ "qmlprofilerplugin.cpp",
+ "qmlprofilerplugin.h",
+ "qmlprofilerprocessedmodel.cpp",
+ "qmlprofilerprocessedmodel.h",
+ "qmlprofilersimplemodel.cpp",
+ "qmlprofilersimplemodel.h",
+ "qmlprofilerstatemanager.cpp",
+ "qmlprofilerstatemanager.h",
+ "qmlprofilerstatewidget.cpp",
+ "qmlprofilerstatewidget.h",
+ "qmlprofilertimelinemodelproxy.cpp",
+ "qmlprofilertimelinemodelproxy.h",
+ "qmlprofilertool.cpp",
+ "qmlprofilertool.h",
+ "qmlprofilertreeview.cpp",
+ "qmlprofilertreeview.h",
+ "qmlprofilertracefile.cpp",
+ "qmlprofilertracefile.h",
+ "qmlprofilertraceview.cpp",
+ "qmlprofilertraceview.h",
+ "qmlprofilerviewmanager.cpp",
+ "qmlprofilerviewmanager.h",
+ "qv8profilereventview.h",
+ "qv8profilereventview.cpp",
+ "qv8profilerdatamodel.cpp",
+ "qv8profilerdatamodel.h",
+ "timelinerenderer.cpp",
+ "timelinerenderer.h",
+ "canvas/qdeclarativecanvas.cpp",
+ "canvas/qdeclarativecanvas_p.h",
+ "canvas/qdeclarativecanvastimer.cpp",
+ "canvas/qdeclarativecanvastimer_p.h",
+ "canvas/qdeclarativecontext2d.cpp",
+ "canvas/qdeclarativecontext2d_p.h",
+ "canvas/qmlprofilercanvas.cpp",
+ "canvas/qmlprofilercanvas.h",
+ "qml/Detail.qml",
+ "qml/Label.qml",
+ "qml/MainView.qml",
+ "qml/Overview.js",
+ "qml/Overview.qml",
+ "qml/RangeDetails.qml",
+ "qml/RangeMover.qml",
+ "qml/SelectionRange.qml",
+ "qml/SelectionRangeDetails.qml",
+ "qml/TimeDisplay.qml",
+ "qml/TimeMarks.qml",
+ "qml/qmlprofiler.qrc",
+ ]
+}
diff --git a/plugins/qmlprofiler/qmlprofiler_dependencies.pri b/plugins/qmlprofiler/qmlprofiler_dependencies.pri
new file mode 100644
index 0000000000..47e65ba235
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofiler_dependencies.pri
@@ -0,0 +1,13 @@
+QTC_PLUGIN_NAME = QmlProfiler
+QTC_LIB_DEPENDS += \
+ qmldebug \
+ extensionsystem
+QTC_PLUGIN_DEPENDS += \
+ analyzerbase \
+ coreplugin \
+ debugger \
+ projectexplorer \
+ qmlprojectmanager \
+ qmljstools \
+ qtsupport \
+ texteditor
diff --git a/plugins/qmlprofiler/qmlprofiler_global.h b/plugins/qmlprofiler/qmlprofiler_global.h
new file mode 100644
index 0000000000..0bddac54a3
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofiler_global.h
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILER_GLOBAL_H
+#define QMLPROFILER_GLOBAL_H
+
+#include <QtGlobal>
+
+#if defined(QMLPROFILER_LIBRARY)
+# define QMLPROFILER_EXPORT Q_DECL_EXPORT
+#else
+# define QMLPROFILER_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // QMLPROFILER_GLOBAL_H
+
diff --git a/plugins/qmlprofiler/qmlprofilerattachdialog.cpp b/plugins/qmlprofiler/qmlprofilerattachdialog.cpp
new file mode 100644
index 0000000000..53eb3f68a0
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerattachdialog.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilerattachdialog.h"
+
+#include <projectexplorer/kitchooser.h>
+
+#include <coreplugin/id.h>
+
+#include <QSpinBox>
+#include <QDialogButtonBox>
+#include <QFormLayout>
+#include <QPushButton>
+
+using namespace ProjectExplorer;
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerAttachDialogPrivate
+{
+public:
+ QSpinBox *portSpinBox;
+ KitChooser *kitChooser;
+};
+
+QmlProfilerAttachDialog::QmlProfilerAttachDialog(QWidget *parent) :
+ QDialog(parent),
+ d(new QmlProfilerAttachDialogPrivate)
+{
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ setWindowTitle(tr("Start QML Profiler"));
+
+ d->kitChooser = new KitChooser(this);
+ d->kitChooser->populate();
+
+ d->portSpinBox = new QSpinBox(this);
+ d->portSpinBox->setMaximum(65535);
+ d->portSpinBox->setValue(3768);
+
+ QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
+ buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
+ buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
+
+ QFormLayout *formLayout = new QFormLayout();
+ formLayout->addRow(tr("Kit:"), d->kitChooser);
+ formLayout->addRow(tr("&Port:"), d->portSpinBox);
+
+ QVBoxLayout *verticalLayout = new QVBoxLayout(this);
+ verticalLayout->addLayout(formLayout);
+ verticalLayout->addWidget(buttonBox);
+
+ connect(buttonBox, SIGNAL(accepted()), SLOT(accept()));
+ connect(buttonBox, SIGNAL(rejected()), SLOT(reject()));
+}
+
+QmlProfilerAttachDialog::~QmlProfilerAttachDialog()
+{
+ delete d;
+}
+
+int QmlProfilerAttachDialog::port() const
+{
+ return d->portSpinBox->value();
+}
+
+void QmlProfilerAttachDialog::setPort(const int port)
+{
+ d->portSpinBox->setValue(port);
+}
+
+ProjectExplorer::Kit *QmlProfilerAttachDialog::kit() const
+{
+ return d->kitChooser->currentKit();
+}
+
+void QmlProfilerAttachDialog::setKitId(const Core::Id &id)
+{
+ d->kitChooser->setCurrentKitId(id);
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/plugins/qmlprofiler/qmlprofilerattachdialog.h b/plugins/qmlprofiler/qmlprofilerattachdialog.h
new file mode 100644
index 0000000000..739ba8bd1f
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerattachdialog.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERATTACHDIALOG_H
+#define QMLPROFILERATTACHDIALOG_H
+
+#include <QDialog>
+
+namespace Core { class Id; }
+namespace ProjectExplorer { class Kit; }
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerAttachDialogPrivate;
+class QmlProfilerAttachDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit QmlProfilerAttachDialog(QWidget *parent = 0);
+ ~QmlProfilerAttachDialog();
+
+ int port() const;
+ void setPort(const int port);
+
+ ProjectExplorer::Kit *kit() const;
+ void setKitId(const Core::Id &id);
+
+private:
+ QmlProfilerAttachDialogPrivate *d;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILERATTACHDIALOG_H
diff --git a/plugins/qmlprofiler/qmlprofilerattachdialog.ui b/plugins/qmlprofiler/qmlprofilerattachdialog.ui
new file mode 100644
index 0000000000..c920c4d160
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerattachdialog.ui
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QmlProfiler::Internal::QmlProfilerAttachDialog</class>
+ <widget class="QDialog" name="QmlProfiler::Internal::QmlProfilerAttachDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>203</width>
+ <height>136</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>QML Profiler</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="addressLabel">
+ <property name="text">
+ <string>&amp;Host:</string>
+ </property>
+ <property name="buddy">
+ <cstring>addressLineEdit</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="addressLineEdit">
+ <property name="text">
+ <string>localhost</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="portLabel">
+ <property name="text">
+ <string>&amp;Port:</string>
+ </property>
+ <property name="buddy">
+ <cstring>portSpinBox</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="portSpinBox">
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>65535</number>
+ </property>
+ <property name="value">
+ <number>3768</number>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Sys&amp;root:</string>
+ </property>
+ <property name="buddy">
+ <cstring>sysrootChooser</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="Utils::PathChooser" name="sysrootChooser" native="true"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>Utils::PathChooser</class>
+ <extends>QWidget</extends>
+ <header location="global">utils/pathchooser.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>QmlProfiler::Internal::QmlProfilerAttachDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>QmlProfiler::Internal::QmlProfilerAttachDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/plugins/qmlprofiler/qmlprofilerclientmanager.cpp b/plugins/qmlprofiler/qmlprofilerclientmanager.cpp
new file mode 100644
index 0000000000..e1855fad95
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerclientmanager.cpp
@@ -0,0 +1,431 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilerclientmanager.h"
+#include "qmlprofilertool.h"
+#include "qmlprofilerplugin.h"
+
+#include <qmldebug/qmldebugclient.h>
+#include <qmldebug/qmlprofilertraceclient.h>
+#include <qmldebug/qv8profilerclient.h>
+
+#include <utils/qtcassert.h>
+#include <QPointer>
+#include <QTimer>
+#include <QMessageBox>
+
+using namespace QmlDebug;
+using namespace Core;
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerClientManager::QmlProfilerClientManagerPrivate {
+public:
+ QmlProfilerClientManagerPrivate(QmlProfilerClientManager *qq) { Q_UNUSED(qq); }
+
+ QmlProfilerStateManager* profilerState;
+
+ QmlDebugConnection *connection;
+ QPointer<QmlProfilerTraceClient> qmlclientplugin;
+ QPointer<QV8ProfilerClient> v8clientplugin;
+
+ QTimer connectionTimer;
+ int connectionAttempts;
+
+ enum ConnectMode {
+ TcpConnection, OstConnection
+ };
+ ConnectMode connectMode;
+ QString tcpHost;
+ quint64 tcpPort;
+ QString ostDevice;
+ QString sysroot;
+
+ bool v8DataReady;
+ bool qmlDataReady;
+};
+
+QmlProfilerClientManager::QmlProfilerClientManager(QObject *parent) :
+ QObject(parent), d(new QmlProfilerClientManagerPrivate(this))
+{
+ setObjectName(QLatin1String("QML Profiler Connections"));
+
+ d->profilerState = 0;
+
+ d->connection = 0;
+ d->connectionAttempts = 0;
+ d->v8DataReady = false;
+ d->qmlDataReady = false;
+
+ d->connectionTimer.setInterval(200);
+ connect(&d->connectionTimer, SIGNAL(timeout()), SLOT(tryToConnect()));
+}
+
+QmlProfilerClientManager::~QmlProfilerClientManager()
+{
+ disconnectClientSignals();
+ delete d->connection;
+ delete d->qmlclientplugin.data();
+ delete d->v8clientplugin.data();
+
+ delete d;
+}
+////////////////////////////////////////////////////////////////
+// Interface
+void QmlProfilerClientManager::setTcpConnection(QString host, quint64 port)
+{
+ d->connectMode = QmlProfilerClientManagerPrivate::TcpConnection;
+ d->tcpHost = host;
+ d->tcpPort = port;
+}
+
+void QmlProfilerClientManager::setOstConnection(QString ostDevice)
+{
+ d->connectMode = QmlProfilerClientManagerPrivate::OstConnection;
+ d->ostDevice = ostDevice;
+}
+
+void QmlProfilerClientManager::clearBufferedData()
+{
+ if (d->qmlclientplugin)
+ d->qmlclientplugin.data()->clearData();
+ if (d->v8clientplugin)
+ d->v8clientplugin.data()->clearData();
+}
+
+void QmlProfilerClientManager::discardPendingData()
+{
+ if (d->connection)
+ d->connection->flush();
+ clearBufferedData();
+}
+
+////////////////////////////////////////////////////////////////
+// Internal
+void QmlProfilerClientManager::connectClient(quint16 port)
+{
+ if (d->connection)
+ delete d->connection;
+ d->connection = new QmlDebugConnection;
+ enableServices();
+ connect(d->connection, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
+ this, SLOT(connectionStateChanged()));
+ d->connectionTimer.start();
+ d->tcpPort = port;
+}
+
+void QmlProfilerClientManager::enableServices()
+{
+ QTC_ASSERT(d->profilerState, return);
+
+ disconnectClientSignals();
+ d->profilerState->setServerRecording(false); // false by default (will be set to true when connected)
+ delete d->qmlclientplugin.data();
+ d->qmlclientplugin = new QmlProfilerTraceClient(d->connection);
+ delete d->v8clientplugin.data();
+ d->v8clientplugin = new QV8ProfilerClient(d->connection);
+ connectClientSignals();
+}
+
+void QmlProfilerClientManager::connectClientSignals()
+{
+ QTC_ASSERT(d->profilerState, return);
+ if (d->qmlclientplugin) {
+ connect(d->qmlclientplugin.data(), SIGNAL(complete()),
+ this, SLOT(qmlComplete()));
+ connect(d->qmlclientplugin.data(),
+ SIGNAL(range(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)),
+ this,
+ SIGNAL(addRangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)));
+ connect(d->qmlclientplugin.data(), SIGNAL(traceFinished(qint64)),
+ this, SIGNAL(traceFinished(qint64)));
+ connect(d->qmlclientplugin.data(), SIGNAL(traceStarted(qint64)),
+ this, SIGNAL(traceStarted(qint64)));
+ connect(d->qmlclientplugin.data(), SIGNAL(frame(qint64,int,int)),
+ this, SIGNAL(addFrameEvent(qint64,int,int)));
+ connect(d->qmlclientplugin.data(), SIGNAL(enabledChanged()),
+ d->qmlclientplugin.data(), SLOT(sendRecordingStatus()));
+ // fixme: this should be unified for both clients
+ connect(d->qmlclientplugin.data(), SIGNAL(recordingChanged(bool)),
+ d->profilerState, SLOT(setServerRecording(bool)));
+ }
+ if (d->v8clientplugin) {
+ connect(d->v8clientplugin.data(), SIGNAL(complete()), this, SLOT(v8Complete()));
+ connect(d->v8clientplugin.data(),
+ SIGNAL(v8range(int,QString,QString,int,double,double)),
+ this,
+ SIGNAL(addV8Event(int,QString,QString,int,double,double)));
+ connect(d->v8clientplugin.data(), SIGNAL(enabledChanged()),
+ d->v8clientplugin.data(), SLOT(sendRecordingStatus()));
+ }
+}
+
+void QmlProfilerClientManager::disconnectClientSignals()
+{
+ if (d->qmlclientplugin) {
+ disconnect(d->qmlclientplugin.data(), SIGNAL(complete()),
+ this, SLOT(qmlComplete()));
+ disconnect(d->qmlclientplugin.data(),
+ SIGNAL(range(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)),
+ this,
+ SIGNAL(addRangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)));
+ disconnect(d->qmlclientplugin.data(), SIGNAL(traceFinished(qint64)),
+ this, SIGNAL(traceFinished(qint64)));
+ disconnect(d->qmlclientplugin.data(), SIGNAL(traceStarted(qint64)),
+ this, SIGNAL(traceStarted(qint64)));
+ disconnect(d->qmlclientplugin.data(), SIGNAL(frame(qint64,int,int)),
+ this, SIGNAL(addFrameEvent(qint64,int,int)));
+ disconnect(d->qmlclientplugin.data(), SIGNAL(enabledChanged()),
+ d->qmlclientplugin.data(), SLOT(sendRecordingStatus()));
+ // fixme: this should be unified for both clients
+ disconnect(d->qmlclientplugin.data(), SIGNAL(recordingChanged(bool)),
+ d->profilerState, SLOT(setServerRecording(bool)));
+ }
+ if (d->v8clientplugin) {
+ disconnect(d->v8clientplugin.data(), SIGNAL(complete()), this, SLOT(v8Complete()));
+ disconnect(d->v8clientplugin.data(),
+ SIGNAL(v8range(int,QString,QString,int,double,double)),
+ this,
+ SIGNAL(addV8Event(int,QString,QString,int,double,double)));
+ disconnect(d->v8clientplugin.data(), SIGNAL(enabledChanged()),
+ d->v8clientplugin.data(), SLOT(sendRecordingStatus()));
+ }
+}
+
+void QmlProfilerClientManager::connectToClient()
+{
+ if (!d->connection || d->connection->state() != QAbstractSocket::UnconnectedState)
+ return;
+
+ QmlProfilerTool::logStatus(QString::fromLatin1("QML Profiler: Connecting to %1:%2 ...")
+ .arg(d->tcpHost, QString::number(d->tcpPort)));
+ d->connection->connectToHost(d->tcpHost, d->tcpPort);
+}
+
+bool QmlProfilerClientManager::isConnected() const
+{
+ return d->connection && d->connection->isConnected();
+}
+
+void QmlProfilerClientManager::disconnectClient()
+{
+ // this might be actually be called indirectly by QDDConnectionPrivate::readyRead(), therefore allow
+ // method to complete before deleting object
+ if (d->connection) {
+ d->connection->deleteLater();
+ d->connection = 0;
+ }
+}
+
+void QmlProfilerClientManager::tryToConnect()
+{
+ ++d->connectionAttempts;
+
+ if (d->connection && d->connection->isConnected()) {
+ d->connectionTimer.stop();
+ d->connectionAttempts = 0;
+ } else if (d->connectionAttempts == 50) {
+ d->connectionTimer.stop();
+ d->connectionAttempts = 0;
+
+ QMessageBox *infoBox = QmlProfilerTool::requestMessageBox();
+ infoBox->setIcon(QMessageBox::Critical);
+ infoBox->setWindowTitle(tr("Qt Creator"));
+ infoBox->setText(tr("Could not connect to the in-process QML profiler.\n"
+ "Do you want to retry?"));
+ infoBox->setStandardButtons(QMessageBox::Retry |
+ QMessageBox::Cancel |
+ QMessageBox::Help);
+ infoBox->setDefaultButton(QMessageBox::Retry);
+ infoBox->setModal(true);
+
+ connect(infoBox, SIGNAL(finished(int)),
+ this, SLOT(retryMessageBoxFinished(int)));
+
+ infoBox->show();
+ } else {
+ connectToClient();
+ }
+}
+
+void QmlProfilerClientManager::connectionStateChanged()
+{
+ if (!d->connection)
+ return;
+ switch (d->connection->state()) {
+ case QAbstractSocket::UnconnectedState:
+ {
+ if (QmlProfilerPlugin::debugOutput)
+ qWarning("QML Profiler: disconnected");
+ disconnectClient();
+ emit connectionClosed();
+ break;
+ }
+ case QAbstractSocket::HostLookupState:
+ break;
+ case QAbstractSocket::ConnectingState: {
+ if (QmlProfilerPlugin::debugOutput)
+ qWarning("QML Profiler: Connecting to debug server ...");
+ break;
+ }
+ case QAbstractSocket::ConnectedState:
+ {
+ if (QmlProfilerPlugin::debugOutput)
+ qWarning("QML Profiler: connected and running");
+ // notify the client recording status
+ clientRecordingChanged();
+ break;
+ }
+ case QAbstractSocket::ClosingState:
+ if (QmlProfilerPlugin::debugOutput)
+ qWarning("QML Profiler: closing ...");
+ break;
+ case QAbstractSocket::BoundState:
+ case QAbstractSocket::ListeningState:
+ break;
+ }
+}
+
+void QmlProfilerClientManager::retryMessageBoxFinished(int result)
+{
+ switch (result) {
+ case QMessageBox::Retry: {
+ d->connectionAttempts = 0;
+ d->connectionTimer.start();
+ break;
+ }
+ case QMessageBox::Help: {
+ QmlProfilerTool::handleHelpRequest(QLatin1String("qthelp://org.qt-project.qtcreator/doc/creator-debugging-qml.html"));
+ // fall through
+ }
+ default: {
+ if (d->connection)
+ QmlProfilerTool::logStatus(QLatin1String("QML Profiler: Failed to connect! ") + d->connection->errorString());
+ else
+ QmlProfilerTool::logStatus(QLatin1String("QML Profiler: Failed to connect!"));
+
+ emit connectionFailed();
+ break;
+ }
+ }
+}
+
+void QmlProfilerClientManager::qmlComplete()
+{
+ d->qmlDataReady = true;
+ if (!d->v8clientplugin || d->v8clientplugin.data()->status() != QmlDebug::Enabled || d->v8DataReady) {
+ emit dataReadyForProcessing();
+ // once complete is sent, reset the flags
+ d->qmlDataReady = false;
+ d->v8DataReady = false;
+ }
+}
+
+void QmlProfilerClientManager::v8Complete()
+{
+ d->v8DataReady = true;
+ if (!d->qmlclientplugin || d->qmlclientplugin.data()->status() != QmlDebug::Enabled || d->qmlDataReady) {
+ emit dataReadyForProcessing();
+ // once complete is sent, reset the flags
+ d->v8DataReady = false;
+ d->qmlDataReady = false;
+ }
+}
+
+void QmlProfilerClientManager::stopClientsRecording()
+{
+ if (d->qmlclientplugin)
+ d->qmlclientplugin.data()->setRecording(false);
+ if (d->v8clientplugin)
+ d->v8clientplugin.data()->setRecording(false);
+}
+
+////////////////////////////////////////////////////////////////
+// Profiler State
+void QmlProfilerClientManager::registerProfilerStateManager( QmlProfilerStateManager *profilerState )
+{
+ if (d->profilerState) {
+ disconnect(d->profilerState, SIGNAL(stateChanged()),
+ this, SLOT(profilerStateChanged()));
+ disconnect(d->profilerState, SIGNAL(clientRecordingChanged()),
+ this, SLOT(clientRecordingChanged()));
+ disconnect(d->profilerState, SIGNAL(serverRecordingChanged()),
+ this, SLOT(serverRecordingChanged()));
+ }
+
+ d->profilerState = profilerState;
+
+ // connect
+ if (d->profilerState) {
+ connect(d->profilerState, SIGNAL(stateChanged()),
+ this, SLOT(profilerStateChanged()));
+ connect(d->profilerState, SIGNAL(clientRecordingChanged()),
+ this, SLOT(clientRecordingChanged()));
+ connect(d->profilerState, SIGNAL(serverRecordingChanged()),
+ this, SLOT(serverRecordingChanged()));
+ }
+}
+
+void QmlProfilerClientManager::profilerStateChanged()
+{
+ QTC_ASSERT(d->profilerState, return);
+ switch (d->profilerState->currentState()) {
+ case QmlProfilerStateManager::AppStopRequested :
+ if (d->profilerState->serverRecording())
+ stopClientsRecording();
+ else
+ d->profilerState->setCurrentState(QmlProfilerStateManager::AppReadyToStop);
+ break;
+ default:
+ break;
+ }
+}
+
+void QmlProfilerClientManager::clientRecordingChanged()
+{
+ QTC_ASSERT(d->profilerState, return);
+ if (d->profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
+ if (d->qmlclientplugin)
+ d->qmlclientplugin.data()->setRecording(d->profilerState->clientRecording());
+ if (d->v8clientplugin)
+ d->v8clientplugin.data()->setRecording(d->profilerState->clientRecording());
+ }
+}
+
+void QmlProfilerClientManager::serverRecordingChanged()
+{
+ if (d->profilerState->serverRecording()) {
+ d->v8DataReady = false;
+ d->qmlDataReady = false;
+ }
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/plugins/qmlprofiler/qmlprofilerclientmanager.h b/plugins/qmlprofiler/qmlprofilerclientmanager.h
new file mode 100644
index 0000000000..333ef17820
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerclientmanager.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERCLIENTMANAGER_H
+#define QMLPROFILERCLIENTMANAGER_H
+
+#include <QObject>
+#include <QStringList>
+
+#include "qmlprofilerstatemanager.h"
+#include <qmldebug/qmlprofilereventlocation.h>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerClientManager : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QmlProfilerClientManager(QObject *parent = 0);
+ ~QmlProfilerClientManager();
+
+ void registerProfilerStateManager(QmlProfilerStateManager *profilerState);
+
+ void setTcpConnection(QString host, quint64 port);
+ void setOstConnection(QString ostDevice);
+
+ void clearBufferedData();
+ void discardPendingData();
+ bool isConnected() const;
+
+signals:
+ void connectionFailed();
+ void connectionClosed();
+
+ // data
+ void addRangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation);
+ void addV8Event(int,QString,QString,int,double,double);
+ void addFrameEvent(qint64,int,int);
+ void traceStarted(qint64);
+ void traceFinished(qint64);
+ void dataReadyForProcessing();
+
+public slots:
+ void connectClient(quint16 port);
+ void disconnectClient();
+
+private slots:
+ void tryToConnect();
+ void connectionStateChanged();
+ void retryMessageBoxFinished(int result);
+
+ void qmlComplete();
+ void v8Complete();
+
+ void profilerStateChanged();
+ void clientRecordingChanged();
+ void serverRecordingChanged();
+
+private:
+ class QmlProfilerClientManagerPrivate;
+ QmlProfilerClientManagerPrivate *d;
+
+ void connectToClient();
+
+ void enableServices();
+ void connectClientSignals();
+ void disconnectClientSignals();
+
+ void stopClientsRecording();
+};
+
+}
+}
+
+#endif // QMLPROFILERCLIENTMANAGER_H
diff --git a/plugins/qmlprofiler/qmlprofilerconstants.h b/plugins/qmlprofiler/qmlprofilerconstants.h
new file mode 100644
index 0000000000..4e22f43859
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerconstants.h
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERCONSTANTS_H
+#define QMLPROFILERCONSTANTS_H
+
+namespace QmlProfiler {
+namespace Constants {
+
+const char ATTACH[] = "Menu.Analyzer.Attach";
+const char TraceFileExtension[] = ".qtd";
+
+} // namespace Constants
+} // namespace QmlProfiler
+
+#endif // QMLPROFILERCONSTANTS_H
diff --git a/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp b/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp
new file mode 100644
index 0000000000..0b078f9c86
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp
@@ -0,0 +1,211 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilerdetailsrewriter.h"
+
+#include <qmljs/parser/qmljsast_p.h>
+#include <qmljs/qmljsmodelmanagerinterface.h>
+#include <qmljstools/qmljsmodelmanager.h>
+
+#include <utils/qtcassert.h>
+
+namespace QmlProfiler {
+namespace Internal {
+
+struct PendingEvent {
+ QmlDebug::QmlEventLocation location;
+ QString localFile;
+ int requestId;
+};
+
+class PropertyVisitor: protected QmlJS::AST::Visitor
+{
+ QmlJS::AST::Node * _lastValidNode;
+ unsigned _line;
+ unsigned _col;
+public:
+ QmlJS::AST::Node * operator()(QmlJS::AST::Node *node, unsigned line, unsigned col)
+ {
+ _line = line;
+ _col = col;
+ _lastValidNode = 0;
+ accept(node);
+ return _lastValidNode;
+ }
+
+protected:
+ using QmlJS::AST::Visitor::visit;
+
+ void accept(QmlJS::AST::Node *node)
+ {
+ if (node)
+ node->accept(this);
+ }
+
+ bool containsLocation(QmlJS::AST::SourceLocation start, QmlJS::AST::SourceLocation end)
+ {
+ return (_line > start.startLine || (_line == start.startLine && _col >= start.startColumn)) &&
+ (_line < end.startLine || (_line == end.startLine && _col <= end.startColumn));
+ }
+
+
+ virtual bool preVisit(QmlJS::AST::Node *node)
+ {
+ if (QmlJS::AST::cast<QmlJS::AST::UiQualifiedId *>(node))
+ return false;
+ return containsLocation(node->firstSourceLocation(), node->lastSourceLocation());
+ }
+
+ virtual bool visit(QmlJS::AST::UiScriptBinding *ast)
+ {
+ _lastValidNode = ast;
+ return true;
+ }
+
+ virtual bool visit(QmlJS::AST::UiPublicMember *ast)
+ {
+ _lastValidNode = ast;
+ return true;
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////
+
+class QmlProfilerDetailsRewriter::QmlProfilerDetailsRewriterPrivate
+{
+public:
+ QmlProfilerDetailsRewriterPrivate(QmlProfilerDetailsRewriter *qq,
+ Utils::FileInProjectFinder *fileFinder)
+ : m_projectFinder(fileFinder), q(qq) {}
+ ~QmlProfilerDetailsRewriterPrivate() {}
+
+ QList <PendingEvent> m_pendingEvents;
+ QStringList m_pendingDocs;
+ Utils::FileInProjectFinder *m_projectFinder;
+
+ QmlProfilerDetailsRewriter *q;
+};
+
+QmlProfilerDetailsRewriter::QmlProfilerDetailsRewriter(
+ QObject *parent, Utils::FileInProjectFinder *fileFinder)
+ : QObject(parent), d(new QmlProfilerDetailsRewriterPrivate(this, fileFinder))
+{ }
+
+QmlProfilerDetailsRewriter::~QmlProfilerDetailsRewriter()
+{
+ delete d;
+}
+
+void QmlProfilerDetailsRewriter::requestDetailsForLocation(int requestId,
+ const QmlDebug::QmlEventLocation &location)
+{
+ const QString localFile = d->m_projectFinder->findFile(location.filename);
+ QFileInfo fileInfo(localFile);
+ if (!fileInfo.exists() || !fileInfo.isReadable())
+ return;
+ if (!QmlJS::Document::isQmlLikeLanguage(QmlJSTools::languageOfFile(localFile)))
+ return;
+
+ PendingEvent ev = {location, localFile, requestId};
+ d->m_pendingEvents << ev;
+ if (!d->m_pendingDocs.contains(localFile)) {
+ if (d->m_pendingDocs.isEmpty())
+ connect(QmlJS::ModelManagerInterface::instance(),
+ SIGNAL(documentUpdated(QmlJS::Document::Ptr)),
+ this,
+ SLOT(documentReady(QmlJS::Document::Ptr)));
+
+ d->m_pendingDocs << localFile;
+ }
+}
+
+void QmlProfilerDetailsRewriter::reloadDocuments()
+{
+ if (!d->m_pendingDocs.isEmpty())
+ QmlJS::ModelManagerInterface::instance()->updateSourceFiles(d->m_pendingDocs, false);
+ else
+ emit eventDetailsChanged();
+}
+
+void QmlProfilerDetailsRewriter::rewriteDetailsForLocation(QTextStream &textDoc,
+ QmlJS::Document::Ptr doc, int requestId, const QmlDebug::QmlEventLocation &location)
+{
+ PropertyVisitor propertyVisitor;
+ QmlJS::AST::Node *node = propertyVisitor(doc->ast(), location.line, location.column);
+
+ if (!node)
+ return;
+
+ qint64 startPos = node->firstSourceLocation().begin();
+ qint64 len = node->lastSourceLocation().end() - startPos;
+
+ textDoc.seek(startPos);
+ QString details = textDoc.read(len).replace(QLatin1Char('\n'), QLatin1Char(' ')).simplified();
+
+ emit rewriteDetailsString(requestId, details);
+}
+
+void QmlProfilerDetailsRewriter::clearRequests()
+{
+ d->m_pendingDocs.clear();
+}
+
+void QmlProfilerDetailsRewriter::documentReady(QmlJS::Document::Ptr doc)
+{
+ // this could be triggered by an unrelated reload in Creator
+ if (!d->m_pendingDocs.contains(doc->fileName()))
+ return;
+
+ // if the file could not be opened this slot is still triggered but source will be an empty string
+ QString source = doc->source();
+ if (!source.isEmpty()) {
+ QTextStream st(&source, QIODevice::ReadOnly);
+
+ for (int i = d->m_pendingEvents.count()-1; i>=0; i--) {
+ PendingEvent ev = d->m_pendingEvents[i];
+ if (ev.localFile == doc->fileName()) {
+ d->m_pendingEvents.removeAt(i);
+ rewriteDetailsForLocation(st, doc, ev.requestId, ev.location);
+ }
+ }
+ }
+
+ d->m_pendingDocs.removeOne(doc->fileName());
+
+ if (d->m_pendingDocs.isEmpty()) {
+ disconnect(QmlJS::ModelManagerInterface::instance(),
+ SIGNAL(documentUpdated(QmlJS::Document::Ptr)),
+ this,
+ SLOT(documentReady(QmlJS::Document::Ptr)));
+ emit eventDetailsChanged();
+ }
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h b/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h
new file mode 100644
index 0000000000..21e94a1bf3
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERDETAILSREWRITER_H
+#define QMLPROFILERDETAILSREWRITER_H
+
+#include <QObject>
+
+#include <qmldebug/qmlprofilereventlocation.h>
+#include <qmljs/qmljsdocument.h>
+#include <utils/fileinprojectfinder.h>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerDetailsRewriter : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QmlProfilerDetailsRewriter(QObject *parent, Utils::FileInProjectFinder *fileFinder);
+ ~QmlProfilerDetailsRewriter();
+
+ void clearRequests();
+
+private:
+ void rewriteDetailsForLocation(QTextStream &textDoc, QmlJS::Document::Ptr doc, int requestId,
+ const QmlDebug::QmlEventLocation &location);
+
+public slots:
+ void requestDetailsForLocation(int requestId, const QmlDebug::QmlEventLocation &location);
+ void reloadDocuments();
+ void documentReady(QmlJS::Document::Ptr doc);
+signals:
+ void rewriteDetailsString(int requestId, const QString &details);
+ void eventDetailsChanged();
+private:
+ class QmlProfilerDetailsRewriterPrivate;
+ QmlProfilerDetailsRewriterPrivate *d;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILERDETAILSREWRITER_H
diff --git a/plugins/qmlprofiler/qmlprofilerengine.cpp b/plugins/qmlprofiler/qmlprofilerengine.cpp
new file mode 100644
index 0000000000..9b71068c37
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerengine.cpp
@@ -0,0 +1,388 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilerengine.h"
+
+#include "localqmlprofilerrunner.h"
+
+#include <analyzerbase/analyzermanager.h>
+#include <coreplugin/icore.h>
+#include <debugger/debuggerrunconfigurationaspect.h>
+#include <utils/qtcassert.h>
+#include <coreplugin/helpmanager.h>
+#include <projectexplorer/projectexplorerconstants.h>
+#include <projectexplorer/kitinformation.h>
+#include <projectexplorer/target.h>
+#include <qmlprojectmanager/qmlprojectrunconfiguration.h>
+#include <qmlprojectmanager/qmlprojectplugin.h>
+#include <projectexplorer/environmentaspect.h>
+#include <projectexplorer/localapplicationruncontrol.h>
+#include <projectexplorer/localapplicationrunconfiguration.h>
+#include <qmldebug/qmloutputparser.h>
+
+#include <QMainWindow>
+#include <QMessageBox>
+#include <QTimer>
+#include <QTcpServer>
+
+using namespace Analyzer;
+using namespace ProjectExplorer;
+
+namespace QmlProfiler {
+namespace Internal {
+
+//
+// QmlProfilerEnginePrivate
+//
+
+class QmlProfilerEngine::QmlProfilerEnginePrivate
+{
+public:
+ QmlProfilerEnginePrivate(QmlProfilerEngine *qq, const AnalyzerStartParameters &sp) : q(qq), m_runner(0), sp(sp) {}
+ ~QmlProfilerEnginePrivate() { delete m_runner; }
+
+ bool attach(const QString &address, uint port);
+ AbstractQmlProfilerRunner *createRunner(ProjectExplorer::RunConfiguration *runConfiguration,
+ QObject *parent);
+
+ QmlProfilerEngine *q;
+
+ QmlProfilerStateManager *m_profilerState;
+
+ AbstractQmlProfilerRunner *m_runner;
+ QTimer m_noDebugOutputTimer;
+ QmlDebug::QmlOutputParser m_outputParser;
+ const AnalyzerStartParameters sp;
+};
+
+AbstractQmlProfilerRunner *
+QmlProfilerEngine::QmlProfilerEnginePrivate::createRunner(ProjectExplorer::RunConfiguration *runConfiguration,
+ QObject *parent)
+{
+ AbstractQmlProfilerRunner *runner = 0;
+ if (!runConfiguration) // attaching
+ return 0;
+
+ QmlProjectManager::QmlProjectRunConfiguration *rc1 =
+ qobject_cast<QmlProjectManager::QmlProjectRunConfiguration *>(runConfiguration);
+ LocalApplicationRunConfiguration *rc2 =
+ qobject_cast<LocalApplicationRunConfiguration *>(runConfiguration);
+ // Supports only local run configurations
+ if (!rc1 && !rc2)
+ return 0;
+
+ ProjectExplorer::EnvironmentAspect *environment
+ = runConfiguration->extraAspect<ProjectExplorer::EnvironmentAspect>();
+ QTC_ASSERT(environment, return 0);
+ LocalQmlProfilerRunner::Configuration conf;
+ if (rc1) {
+ // This is a "plain" .qmlproject.
+ conf.executable = rc1->observerPath();
+ conf.executableArguments = rc1->viewerArguments();
+ conf.workingDirectory = rc1->workingDirectory();
+ conf.environment = environment->environment();
+ } else {
+ // FIXME: Check.
+ conf.executable = rc2->executable();
+ conf.executableArguments = rc2->commandLineArguments();
+ conf.workingDirectory = rc2->workingDirectory();
+ conf.environment = environment->environment();
+ }
+ const ProjectExplorer::IDevice::ConstPtr device =
+ ProjectExplorer::DeviceKitInformation::device(runConfiguration->target()->kit());
+ QTC_ASSERT(device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE, return 0);
+ conf.port = sp.analyzerPort;
+ runner = new LocalQmlProfilerRunner(conf, parent);
+ return runner;
+}
+
+//
+// QmlProfilerEngine
+//
+
+QmlProfilerEngine::QmlProfilerEngine(IAnalyzerTool *tool,
+ const Analyzer::AnalyzerStartParameters &sp,
+ ProjectExplorer::RunConfiguration *runConfiguration)
+ : IAnalyzerEngine(tool, sp, runConfiguration)
+ , d(new QmlProfilerEnginePrivate(this, sp))
+{
+ d->m_profilerState = 0;
+
+ // Only wait 4 seconds for the 'Waiting for connection' on application output, then just try to connect
+ // (application output might be redirected / blocked)
+ d->m_noDebugOutputTimer.setSingleShot(true);
+ d->m_noDebugOutputTimer.setInterval(4000);
+ connect(&d->m_noDebugOutputTimer, SIGNAL(timeout()), this, SLOT(processIsRunning()));
+
+ d->m_outputParser.setNoOutputText(ApplicationLauncher::msgWinCannotRetrieveDebuggingOutput());
+ connect(&d->m_outputParser, SIGNAL(waitingForConnectionOnPort(quint16)),
+ this, SLOT(processIsRunning(quint16)));
+ connect(&d->m_outputParser, SIGNAL(noOutputMessage()),
+ this, SLOT(processIsRunning()));
+ connect(&d->m_outputParser, SIGNAL(errorMessage(QString)),
+ this, SLOT(wrongSetupMessageBox(QString)));
+}
+
+QmlProfilerEngine::~QmlProfilerEngine()
+{
+ if (d->m_profilerState && d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning)
+ stop();
+ delete d;
+}
+
+bool QmlProfilerEngine::start()
+{
+ QTC_ASSERT(d->m_profilerState, return false);
+
+ if (d->m_runner) {
+ delete d->m_runner;
+ d->m_runner = 0;
+ }
+
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStarting);
+
+ if (QmlProjectManager::QmlProjectRunConfiguration *rc =
+ qobject_cast<QmlProjectManager::QmlProjectRunConfiguration *>(runConfiguration())) {
+ if (rc->observerPath().isEmpty()) {
+ QmlProjectManager::QmlProjectPlugin::showQmlObserverToolWarning();
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
+ AnalyzerManager::stopTool();
+ return false;
+ }
+ }
+
+ d->m_runner = d->createRunner(runConfiguration(), this);
+
+ if (LocalQmlProfilerRunner *qmlRunner = qobject_cast<LocalQmlProfilerRunner *>(d->m_runner)) {
+ if (!qmlRunner->hasExecutable()) {
+ showNonmodalWarning(tr("No executable file to launch."));
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
+ AnalyzerManager::stopTool();
+ return false;
+ }
+ }
+
+ if (d->m_runner) {
+ connect(d->m_runner, SIGNAL(stopped()), this, SLOT(processEnded()));
+ connect(d->m_runner, SIGNAL(appendMessage(QString,Utils::OutputFormat)),
+ this, SLOT(logApplicationMessage(QString,Utils::OutputFormat)));
+ d->m_runner->start();
+ d->m_noDebugOutputTimer.start();
+ } else if (d->sp.startMode == StartQmlRemote) {
+ d->m_noDebugOutputTimer.start();
+ } else {
+ emit processRunning(startParameters().analyzerPort);
+ }
+
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppRunning);
+ emit starting(this);
+ return true;
+}
+
+void QmlProfilerEngine::stop()
+{
+ QTC_ASSERT(d->m_profilerState, return);
+
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppRunning : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStopRequested);
+ break;
+ }
+ case QmlProfilerStateManager::AppReadyToStop : {
+ cancelProcess();
+ break;
+ }
+ case QmlProfilerStateManager::AppDying :
+ // valid, but no further action is needed
+ break;
+ default: {
+ const QString message = QString::fromLatin1("Unexpected engine stop from state %1 in %2:%3")
+ .arg(d->m_profilerState->currentStateAsString(), QString::fromLatin1(__FILE__), QString::number(__LINE__));
+ qWarning("%s", qPrintable(message));
+ }
+ break;
+ }
+}
+
+void QmlProfilerEngine::processEnded()
+{
+ QTC_ASSERT(d->m_profilerState, return);
+
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppRunning : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppDying);
+ AnalyzerManager::stopTool();
+
+ emit finished();
+ break;
+ }
+ case QmlProfilerStateManager::AppStopped :
+ case QmlProfilerStateManager::AppKilled :
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
+ break;
+ default: {
+ const QString message = QString::fromLatin1("Process died unexpectedly from state %1 in %2:%3")
+ .arg(d->m_profilerState->currentStateAsString(), QString::fromLatin1(__FILE__), QString::number(__LINE__));
+ qWarning("%s", qPrintable(message));
+}
+ break;
+ }
+}
+
+void QmlProfilerEngine::cancelProcess()
+{
+ QTC_ASSERT(d->m_profilerState, return);
+
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppReadyToStop : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStopped);
+ break;
+ }
+ case QmlProfilerStateManager::AppRunning : {
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppDying);
+ break;
+ }
+ default: {
+ const QString message = QString::fromLatin1("Unexpected process termination requested with state %1 in %2:%3")
+ .arg(d->m_profilerState->currentStateAsString(), QString::fromLatin1(__FILE__), QString::number(__LINE__));
+ qWarning("%s", qPrintable(message));
+ return;
+ }
+ }
+
+ if (d->m_runner)
+ d->m_runner->stop();
+ emit finished();
+}
+
+void QmlProfilerEngine::logApplicationMessage(const QString &msg, Utils::OutputFormat format)
+{
+ emit outputReceived(msg, format);
+ d->m_outputParser.processOutput(msg);
+}
+
+void QmlProfilerEngine::wrongSetupMessageBox(const QString &errorMessage)
+{
+ QMessageBox *infoBox = new QMessageBox(Core::ICore::mainWindow());
+ infoBox->setIcon(QMessageBox::Critical);
+ infoBox->setWindowTitle(tr("Qt Creator"));
+ //: %1 is detailed error message
+ infoBox->setText(tr("Could not connect to the in-process QML debugger:\n%1")
+ .arg(errorMessage));
+ infoBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Help);
+ infoBox->setDefaultButton(QMessageBox::Ok);
+ infoBox->setModal(true);
+
+ connect(infoBox, SIGNAL(finished(int)),
+ this, SLOT(wrongSetupMessageBoxFinished(int)));
+
+ infoBox->show();
+
+ // KILL
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppDying);
+ AnalyzerManager::stopTool();
+ emit finished();
+}
+
+void QmlProfilerEngine::wrongSetupMessageBoxFinished(int button)
+{
+ if (button == QMessageBox::Help) {
+ Core::HelpManager *helpManager = Core::HelpManager::instance();
+ helpManager->handleHelpRequest(QLatin1String("qthelp://org.qt-project.qtcreator/doc/creator-debugging-qml.html"
+ "#setting-up-qml-debugging"));
+ }
+}
+
+void QmlProfilerEngine::showNonmodalWarning(const QString &warningMsg)
+{
+ QMessageBox *noExecWarning = new QMessageBox(Core::ICore::mainWindow());
+ noExecWarning->setIcon(QMessageBox::Warning);
+ noExecWarning->setWindowTitle(tr("QML Profiler"));
+ noExecWarning->setText(warningMsg);
+ noExecWarning->setStandardButtons(QMessageBox::Ok);
+ noExecWarning->setDefaultButton(QMessageBox::Ok);
+ noExecWarning->setModal(false);
+ noExecWarning->show();
+}
+
+void QmlProfilerEngine::notifyRemoteSetupDone(quint16 port)
+{
+ d->m_noDebugOutputTimer.stop();
+ emit processRunning(port);
+}
+
+void QmlProfilerEngine::processIsRunning(quint16 port)
+{
+ d->m_noDebugOutputTimer.stop();
+
+ if (port > 0)
+ emit processRunning(port);
+ else if (d->m_runner)
+ emit processRunning(d->m_runner->debugPort());
+}
+
+////////////////////////////////////////////////////////////////
+// Profiler State
+void QmlProfilerEngine::registerProfilerStateManager( QmlProfilerStateManager *profilerState )
+{
+ // disconnect old
+ if (d->m_profilerState)
+ disconnect(d->m_profilerState, SIGNAL(stateChanged()), this, SLOT(profilerStateChanged()));
+
+ d->m_profilerState = profilerState;
+
+ // connect
+ if (d->m_profilerState)
+ connect(d->m_profilerState, SIGNAL(stateChanged()), this, SLOT(profilerStateChanged()));
+}
+
+void QmlProfilerEngine::profilerStateChanged()
+{
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppReadyToStop : {
+ cancelProcess();
+ break;
+ }
+ case QmlProfilerStateManager::Idle : {
+ // When all the profiling is done, delete the profiler runner
+ // (a new one will be created at start)
+ d->m_noDebugOutputTimer.stop();
+ if (d->m_runner) {
+ delete d->m_runner;
+ d->m_runner = 0;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/plugins/qmlprofiler/qmlprofilerengine.h b/plugins/qmlprofiler/qmlprofilerengine.h
new file mode 100644
index 0000000000..fe8cc47313
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerengine.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERENGINE_H
+#define QMLPROFILERENGINE_H
+
+#include <analyzerbase/ianalyzerengine.h>
+#include "qmlprofilerstatemanager.h"
+#include <utils/outputformat.h>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerEngine : public Analyzer::IAnalyzerEngine
+{
+ Q_OBJECT
+
+public:
+ QmlProfilerEngine(Analyzer::IAnalyzerTool *tool,
+ const Analyzer::AnalyzerStartParameters &sp,
+ ProjectExplorer::RunConfiguration *runConfiguration);
+ ~QmlProfilerEngine();
+
+ void registerProfilerStateManager( QmlProfilerStateManager *profilerState );
+
+ static void showNonmodalWarning(const QString &warningMsg);
+
+ void notifyRemoteSetupDone(quint16 port);
+
+signals:
+ void processRunning(quint16 port);
+ void timeUpdate();
+
+public slots:
+ bool start();
+ void stop();
+
+private slots:
+ void processEnded();
+
+ void cancelProcess();
+ void logApplicationMessage(const QString &msg, Utils::OutputFormat format);
+ void wrongSetupMessageBox(const QString &errorMessage);
+ void wrongSetupMessageBoxFinished(int);
+ void processIsRunning(quint16 port = 0);
+
+private slots:
+ void profilerStateChanged();
+
+private:
+ class QmlProfilerEnginePrivate;
+ QmlProfilerEnginePrivate *d;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILERENGINE_H
diff --git a/plugins/qmlprofiler/qmlprofilereventsmodelproxy.cpp b/plugins/qmlprofiler/qmlprofilereventsmodelproxy.cpp
new file mode 100644
index 0000000000..de98500f69
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilereventsmodelproxy.cpp
@@ -0,0 +1,467 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilereventsmodelproxy.h"
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilersimplemodel.h"
+
+#include <utils/qtcassert.h>
+
+#include <QVector>
+#include <QHash>
+#include <QUrl>
+#include <QString>
+#include <QStack>
+#include <QElapsedTimer>
+
+#include <QDebug>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerEventsModelProxy::QmlProfilerEventsModelProxyPrivate
+{
+public:
+ QmlProfilerEventsModelProxyPrivate(QmlProfilerEventsModelProxy *qq) : q(qq) {}
+ ~QmlProfilerEventsModelProxyPrivate() {}
+
+ QHash<QString, QmlProfilerEventsModelProxy::QmlEventStats> data;
+
+ QmlProfilerModelManager *modelManager;
+ QmlProfilerEventsModelProxy *q;
+
+ QVector<int> acceptedTypes;
+ QSet<QString> eventsInBindingLoop;
+};
+
+QmlProfilerEventsModelProxy::QmlProfilerEventsModelProxy(QmlProfilerModelManager *modelManager, QObject *parent)
+ : QObject(parent), d(new QmlProfilerEventsModelProxyPrivate(this))
+{
+ d->modelManager = modelManager;
+ connect(modelManager->simpleModel(), SIGNAL(changed()), this, SLOT(dataChanged()));
+
+ d->acceptedTypes << QmlDebug::Compiling << QmlDebug::Creating << QmlDebug::Binding << QmlDebug::HandlingSignal;
+}
+
+QmlProfilerEventsModelProxy::~QmlProfilerEventsModelProxy()
+{
+ delete d;
+}
+
+const QList<QmlProfilerEventsModelProxy::QmlEventStats> QmlProfilerEventsModelProxy::getData() const
+{
+ return d->data.values();
+}
+
+void QmlProfilerEventsModelProxy::clear()
+{
+ d->data.clear();
+ d->eventsInBindingLoop.clear();
+}
+
+void QmlProfilerEventsModelProxy::limitToRange(qint64 rangeStart, qint64 rangeEnd)
+{
+ loadData(rangeStart, rangeEnd);
+}
+
+void QmlProfilerEventsModelProxy::dataChanged()
+{
+ loadData();
+}
+
+QSet<QString> QmlProfilerEventsModelProxy::eventsInBindingLoop() const
+{
+ return d->eventsInBindingLoop;
+}
+
+void QmlProfilerEventsModelProxy::loadData(qint64 rangeStart, qint64 rangeEnd)
+{
+ clear();
+
+ qint64 qmlTime = 0;
+ qint64 lastEndTime = 0;
+ QHash <QString, QVector<qint64> > durations;
+
+ const bool checkRanges = (rangeStart != -1) && (rangeEnd != -1);
+
+ const QVector<QmlProfilerSimpleModel::QmlEventData> eventList
+ = d->modelManager->simpleModel()->getEvents();
+
+ // used by binding loop detection
+ typedef QPair<QString, const QmlProfilerSimpleModel::QmlEventData*> CallStackEntry;
+ QStack<CallStackEntry> callStack;
+ callStack.push(CallStackEntry(QString(), 0)); // artificial root
+
+ for (int i = 0; i < eventList.size(); ++i) {
+ const QmlProfilerSimpleModel::QmlEventData *event = &eventList[i];
+
+ if (!d->acceptedTypes.contains(event->eventType))
+ continue;
+
+ if (checkRanges) {
+ if ((event->startTime + event->duration < rangeStart)
+ || (event->startTime > rangeEnd))
+ continue;
+ }
+
+ // put event in hash
+ QString hash = QmlProfilerSimpleModel::getHashString(*event);
+ if (!d->data.contains(hash)) {
+ QmlEventStats stats = {
+ event->bindingType,
+ event->displayName,
+ hash,
+ event->data.join(QLatin1String(" ")),
+ event->location,
+ event->eventType,
+ event->duration,
+ 1, //calls
+ event->duration, //minTime
+ event->duration, // maxTime
+ 0, //timePerCall
+ 0, //percentOfTime
+ 0, //medianTime
+ false //isBindingLoop
+ };
+
+ d->data.insert(hash, stats);
+
+ // for median computing
+ durations.insert(hash, QVector<qint64>());
+ durations[hash].append(event->duration);
+ } else {
+ // update stats
+ QmlEventStats *stats = &d->data[hash];
+
+ stats->duration += event->duration;
+ if (event->duration < stats->minTime)
+ stats->minTime = event->duration;
+ if (event->duration > stats->maxTime)
+ stats->maxTime = event->duration;
+ stats->calls++;
+
+ // for median computing
+ durations[hash].append(event->duration);
+ }
+
+ // qml time computation
+ if (event->startTime > lastEndTime) { // assume parent event if starts before last end
+ qmlTime += event->duration;
+ lastEndTime = event->startTime + event->duration;
+ }
+
+
+ //
+ // binding loop detection
+ //
+ const QmlProfilerSimpleModel::QmlEventData *potentialParent = callStack.top().second;
+ while (potentialParent
+ && !(potentialParent->startTime + potentialParent->duration > event->startTime)) {
+ callStack.pop();
+ potentialParent = callStack.top().second;
+ }
+
+ // check whether event is already in stack
+ bool inLoop = false;
+ for (int ii = 1; ii < callStack.size(); ++ii) {
+ if (callStack.at(ii).first == hash)
+ inLoop = true;
+ if (inLoop)
+ d->eventsInBindingLoop.insert(hash);
+ }
+
+
+ CallStackEntry newEntry(hash, event);
+ callStack.push(newEntry);
+ }
+
+ // post-process: calc mean time, median time, percentoftime
+ foreach (const QString &hash, d->data.keys()) {
+ QmlEventStats* stats = &d->data[hash];
+ if (stats->calls > 0)
+ stats->timePerCall = stats->duration / (double)stats->calls;
+
+ QVector<qint64> eventDurations = durations.value(hash);
+ if (!eventDurations.isEmpty()) {
+ qSort(eventDurations);
+ stats->medianTime = eventDurations.at(eventDurations.count()/2);
+ }
+
+ stats->percentOfTime = stats->duration * 100.0 / qmlTime;
+ }
+
+ // set binding loop flag
+ foreach (const QString &eventHash, d->eventsInBindingLoop)
+ d->data[eventHash].isBindingLoop = true;
+
+ QString rootEventName = tr("<program>");
+ QmlDebug::QmlEventLocation rootEventLocation(rootEventName, 1, 1);
+
+ // insert root event
+ QmlEventStats rootEvent = {
+ 0,
+ rootEventName, //event.displayName,
+ rootEventName, // hash
+ tr("Main Program"), //event.details,
+ rootEventLocation, // location
+ (int)QmlDebug::Binding,
+ qmlTime + 1,
+ 1, //calls
+ qmlTime + 1, //minTime
+ qmlTime + 1, // maxTime
+ qmlTime + 1, //timePerCall
+ 100.0, //percentOfTime
+ qmlTime + 1, //medianTime;
+ false
+ };
+
+ d->data.insert(rootEventName, rootEvent);
+
+ emit dataAvailable();
+}
+
+int QmlProfilerEventsModelProxy::count() const
+{
+ return d->data.count();
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+QmlProfilerEventRelativesModelProxy::QmlProfilerEventRelativesModelProxy(QmlProfilerModelManager *modelManager,
+ QmlProfilerEventsModelProxy *eventsModel,
+ QObject *parent)
+ : QObject(parent)
+{
+ QTC_CHECK(modelManager);
+ m_modelManager = modelManager;
+ connect(modelManager->simpleModel(), SIGNAL(changed()), this, SLOT(dataChanged()));
+
+ QTC_CHECK(eventsModel);
+ m_eventsModel = eventsModel;
+
+ m_acceptedTypes << QmlDebug::Compiling << QmlDebug::Creating << QmlDebug::Binding << QmlDebug::HandlingSignal;
+}
+
+QmlProfilerEventRelativesModelProxy::~QmlProfilerEventRelativesModelProxy()
+{
+}
+
+const QmlProfilerEventRelativesModelProxy::QmlEventRelativesMap QmlProfilerEventRelativesModelProxy::getData(const QString &hash) const
+{
+ if (m_data.contains(hash))
+ return m_data[hash];
+ return QmlEventRelativesMap();
+}
+
+int QmlProfilerEventRelativesModelProxy::count() const
+{
+ return m_data.count();
+}
+
+void QmlProfilerEventRelativesModelProxy::clear()
+{
+ m_data.clear();
+}
+
+void QmlProfilerEventRelativesModelProxy::dataChanged()
+{
+ loadData();
+
+ emit dataAvailable();
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////
+QmlProfilerEventParentsModelProxy::QmlProfilerEventParentsModelProxy(QmlProfilerModelManager *modelManager,
+ QmlProfilerEventsModelProxy *eventsModel,
+ QObject *parent)
+ : QmlProfilerEventRelativesModelProxy(modelManager, eventsModel, parent)
+{}
+
+QmlProfilerEventParentsModelProxy::~QmlProfilerEventParentsModelProxy()
+{}
+
+void QmlProfilerEventParentsModelProxy::loadData()
+{
+ clear();
+ QmlProfilerSimpleModel *simpleModel = m_modelManager->simpleModel();
+ if (simpleModel->isEmpty())
+ return;
+
+ QHash<QString, QmlProfilerSimpleModel::QmlEventData> cachedEvents;
+ QString rootEventName = tr("<program>");
+ QmlProfilerSimpleModel::QmlEventData rootEvent = {
+ rootEventName,
+ QmlDebug::Binding,
+ 0,
+ 0,
+ 0,
+ QStringList() << tr("Main Program"),
+ QmlDebug::QmlEventLocation(rootEventName, 0, 0)
+ };
+ cachedEvents.insert(rootEventName, rootEvent);
+
+ // for level computation
+ QHash<int, qint64> endtimesPerLevel;
+ int level = QmlDebug::Constants::QML_MIN_LEVEL;
+ endtimesPerLevel[0] = 0;
+
+ const QSet<QString> eventsInBindingLoop = m_eventsModel->eventsInBindingLoop();
+
+ // compute parent-child relationship and call count
+ QHash<int, QString> lastParent;
+ //for (int index = fromIndex; index <= toIndex; index++) {
+ const QVector<QmlProfilerSimpleModel::QmlEventData> eventList = simpleModel->getEvents();
+ foreach (const QmlProfilerSimpleModel::QmlEventData &event, eventList) {
+ // whitelist
+ if (!m_acceptedTypes.contains(event.eventType))
+ continue;
+
+ // level computation
+ if (endtimesPerLevel[level] > event.startTime) {
+ level++;
+ } else {
+ while (level > QmlDebug::Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= event.startTime)
+ level--;
+ }
+ endtimesPerLevel[level] = event.startTime + event.duration;
+
+
+ QString parentHash = rootEventName;
+ QString eventHash = QmlProfilerSimpleModel::getHashString(event);
+
+ // save in cache
+ if (!cachedEvents.contains(eventHash))
+ cachedEvents.insert(eventHash, event);
+
+ if (level > QmlDebug::Constants::QML_MIN_LEVEL && lastParent.contains(level-1))
+ parentHash = lastParent[level-1];
+
+ QmlProfilerSimpleModel::QmlEventData *parentEvent = &(cachedEvents[parentHash]);
+
+ // generate placeholder if needed
+ if (!m_data.contains(eventHash))
+ m_data.insert(eventHash, QmlEventRelativesMap());
+
+ if (m_data[eventHash].contains(parentHash)) {
+ QmlEventRelativesData *parent = &(m_data[eventHash][parentHash]);
+ parent->calls++;
+ parent->duration += event.duration;
+ } else {
+ m_data[eventHash].insert(parentHash, QmlEventRelativesData());
+ QmlEventRelativesData *parent = &(m_data[eventHash][parentHash]);
+ parent->displayName = parentEvent->displayName;
+ parent->eventType = parentEvent->eventType;
+ parent->duration = event.duration;
+ parent->calls = 1;
+ parent->details = parentEvent->data.join(QLatin1String(""));
+ parent->isBindingLoop = eventsInBindingLoop.contains(parentHash);
+ }
+
+ // now lastparent is a string with the hash
+ lastParent[level] = eventHash;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+QmlProfilerEventChildrenModelProxy::QmlProfilerEventChildrenModelProxy(QmlProfilerModelManager *modelManager,
+ QmlProfilerEventsModelProxy *eventsModel,
+ QObject *parent)
+ : QmlProfilerEventRelativesModelProxy(modelManager, eventsModel, parent)
+{}
+
+QmlProfilerEventChildrenModelProxy::~QmlProfilerEventChildrenModelProxy()
+{}
+
+void QmlProfilerEventChildrenModelProxy::loadData()
+{
+ clear();
+ QmlProfilerSimpleModel *simpleModel = m_modelManager->simpleModel();
+ if (simpleModel->isEmpty())
+ return;
+
+ QString rootEventName = tr("<program>");
+
+ // for level computation
+ QHash<int, qint64> endtimesPerLevel;
+ int level = QmlDebug::Constants::QML_MIN_LEVEL;
+ endtimesPerLevel[0] = 0;
+
+ const QSet<QString> eventsInBindingLoop = m_eventsModel->eventsInBindingLoop();
+
+ // compute parent-child relationship and call count
+ QHash<int, QString> lastParent;
+ const QVector<QmlProfilerSimpleModel::QmlEventData> eventList = simpleModel->getEvents();
+ foreach (const QmlProfilerSimpleModel::QmlEventData &event, eventList) {
+ // whitelist
+ if (!m_acceptedTypes.contains(event.eventType))
+ continue;
+
+ // level computation
+ if (endtimesPerLevel[level] > event.startTime) {
+ level++;
+ } else {
+ while (level > QmlDebug::Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= event.startTime)
+ level--;
+ }
+ endtimesPerLevel[level] = event.startTime + event.duration;
+
+ QString parentHash = rootEventName;
+ QString eventHash = QmlProfilerSimpleModel::getHashString(event);
+
+ if (level > QmlDebug::Constants::QML_MIN_LEVEL && lastParent.contains(level-1))
+ parentHash = lastParent[level-1];
+
+ // generate placeholder if needed
+ if (!m_data.contains(parentHash))
+ m_data.insert(parentHash, QmlEventRelativesMap());
+
+ if (m_data[parentHash].contains(eventHash)) {
+ QmlEventRelativesData *child = &(m_data[parentHash][eventHash]);
+ child->calls++;
+ child->duration += event.duration;
+ } else {
+ m_data[parentHash].insert(eventHash, QmlEventRelativesData());
+ QmlEventRelativesData *child = &(m_data[parentHash][eventHash]);
+ child->displayName = event.displayName;
+ child->eventType = event.eventType;
+ child->duration = event.duration;
+ child->calls = 1;
+ child->details = event.data.join(QLatin1String(""));
+ child->isBindingLoop = eventsInBindingLoop.contains(parentHash);
+ }
+
+ // now lastparent is a string with the hash
+ lastParent[level] = eventHash;
+ }
+}
+
+
+
+}
+}
diff --git a/plugins/qmlprofiler/qmlprofilereventsmodelproxy.h b/plugins/qmlprofiler/qmlprofilereventsmodelproxy.h
new file mode 100644
index 0000000000..1c6a3ab004
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilereventsmodelproxy.h
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+
+#ifndef QMLPROFILEREVENTSMODELPROXY_H
+#define QMLPROFILEREVENTSMODELPROXY_H
+
+#include "qmlprofilersimplemodel.h"
+#include <QObject>
+#include <qmldebug/qmlprofilereventtypes.h>
+#include <qmldebug/qmlprofilereventlocation.h>
+#include <QHash>
+#include <QVector>
+
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerModelManager;
+
+class QmlProfilerEventsModelProxy : public QObject
+{
+ Q_OBJECT
+public:
+ struct QmlEventStats {
+ int bindingType; // TODO: only makes sense for bindings!
+ QString displayName;
+ QString eventHashStr;
+ QString details;
+ QmlDebug::QmlEventLocation location;
+ int eventType;
+
+ qint64 duration;
+ qint64 calls;
+ qint64 minTime;
+ qint64 maxTime;
+ qint64 timePerCall;
+ double percentOfTime;
+ qint64 medianTime;
+
+ bool isBindingLoop;
+ };
+
+ QmlProfilerEventsModelProxy(QmlProfilerModelManager *modelManager, QObject *parent = 0);
+ ~QmlProfilerEventsModelProxy();
+
+ const QList<QmlEventStats> getData() const;
+ int count() const;
+ void clear();
+
+ void limitToRange(qint64 rangeStart, qint64 rangeEnd);
+
+signals:
+ void dataAvailable();
+
+private:
+ void loadData(qint64 rangeStart = -1, qint64 rangeEnd = -1);
+
+ QSet<QString> eventsInBindingLoop() const;
+
+private slots:
+ void dataChanged();
+
+private:
+ class QmlProfilerEventsModelProxyPrivate;
+ QmlProfilerEventsModelProxyPrivate *d;
+
+ friend class QmlProfilerEventParentsModelProxy;
+ friend class QmlProfilerEventChildrenModelProxy;
+};
+
+class QmlProfilerEventRelativesModelProxy : public QObject
+{
+ Q_OBJECT
+public:
+ struct QmlEventRelativesData {
+ QString displayName;
+ int eventType;
+ qint64 duration;
+ qint64 calls;
+ QString details;
+ bool isBindingLoop;
+ };
+ typedef QHash <QString, QmlEventRelativesData> QmlEventRelativesMap;
+
+ QmlProfilerEventRelativesModelProxy(QmlProfilerModelManager *modelManager,
+ QmlProfilerEventsModelProxy *eventsModel,
+ QObject *parent = 0);
+ ~QmlProfilerEventRelativesModelProxy();
+
+
+ int count() const;
+ void clear();
+
+ const QmlEventRelativesMap getData(const QString &hash) const;
+
+protected:
+ virtual void loadData() = 0;
+
+signals:
+ void dataAvailable();
+
+protected slots:
+ void dataChanged();
+
+protected:
+ QHash <QString, QmlEventRelativesMap> m_data;
+ QmlProfilerModelManager *m_modelManager;
+ QmlProfilerEventsModelProxy *m_eventsModel;
+ QVector <int> m_acceptedTypes;
+};
+
+class QmlProfilerEventParentsModelProxy : public QmlProfilerEventRelativesModelProxy
+{
+ Q_OBJECT
+public:
+ QmlProfilerEventParentsModelProxy(QmlProfilerModelManager *modelManager,
+ QmlProfilerEventsModelProxy *eventsModel,
+ QObject *parent = 0);
+ ~QmlProfilerEventParentsModelProxy();
+
+protected:
+ virtual void loadData();
+signals:
+ void dataAvailable();
+};
+
+class QmlProfilerEventChildrenModelProxy : public QmlProfilerEventRelativesModelProxy
+{
+ Q_OBJECT
+public:
+ QmlProfilerEventChildrenModelProxy(QmlProfilerModelManager *modelManager,
+ QmlProfilerEventsModelProxy *eventsModel,
+ QObject *parent = 0);
+ ~QmlProfilerEventChildrenModelProxy();
+
+protected:
+ virtual void loadData();
+signals:
+ void dataAvailable();
+};
+
+}
+}
+
+#endif
diff --git a/plugins/qmlprofiler/qmlprofilereventview.cpp b/plugins/qmlprofiler/qmlprofilereventview.cpp
new file mode 100644
index 0000000000..0a13ce7748
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilereventview.cpp
@@ -0,0 +1,922 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilereventview.h"
+
+#include <QUrl>
+#include <QHash>
+
+#include <QStandardItem>
+#include <QHeaderView>
+
+#include <QApplication>
+#include <QClipboard>
+
+#include <QContextMenuEvent>
+#include <QDebug>
+
+#include <coreplugin/minisplitter.h>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+
+#include "qmlprofilerviewmanager.h"
+#include "qmlprofilertool.h"
+#include <QMenu>
+
+#include <utils/qtcassert.h>
+
+using namespace QmlDebug;
+
+namespace QmlProfiler {
+namespace Internal {
+
+struct Colors {
+ Colors () {
+ this->bindingLoopBackground = QColor("orange").lighter();
+ }
+
+ QColor bindingLoopBackground;
+};
+
+Q_GLOBAL_STATIC(Colors, colors)
+
+////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class EventsViewItem : public QStandardItem
+{
+public:
+ EventsViewItem(const QString &text) : QStandardItem(text) {}
+
+ virtual bool operator<(const QStandardItem &other) const
+ {
+ if (data().type() == QVariant::String) {
+ // first column
+ if (column() == 0) {
+ return data(FilenameRole).toString() == other.data(FilenameRole).toString() ?
+ data(LineRole).toInt() < other.data(LineRole).toInt() :
+ data(FilenameRole).toString() < other.data(FilenameRole).toString();
+ } else {
+ return data().toString().toLower() < other.data().toString().toLower();
+ }
+ }
+
+ return data().toDouble() < other.data().toDouble();
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class QmlProfilerEventsWidget::QmlProfilerEventsWidgetPrivate
+{
+public:
+ QmlProfilerEventsWidgetPrivate(QmlProfilerEventsWidget *qq):q(qq) {}
+ ~QmlProfilerEventsWidgetPrivate() {}
+
+ QmlProfilerEventsWidget *q;
+
+ Analyzer::IAnalyzerTool *m_profilerTool;
+ QmlProfilerViewManager *m_viewContainer;
+
+ QmlProfilerEventsMainView *m_eventTree;
+ QmlProfilerEventRelativesView *m_eventChildren;
+ QmlProfilerEventRelativesView *m_eventParents;
+
+ QmlProfilerEventsModelProxy *modelProxy;
+ bool globalStats;
+};
+
+QmlProfilerEventsWidget::QmlProfilerEventsWidget(QWidget *parent,
+ Analyzer::IAnalyzerTool *profilerTool,
+ QmlProfilerViewManager *container,
+ QmlProfilerModelManager *profilerModelManager )
+ : QWidget(parent), d(new QmlProfilerEventsWidgetPrivate(this))
+{
+ setObjectName(QLatin1String("QmlProfilerEventsView"));
+
+ d->modelProxy = new QmlProfilerEventsModelProxy(profilerModelManager, this);
+ connect(profilerModelManager, SIGNAL(stateChanged()),
+ this, SLOT(profilerDataModelStateChanged()));
+
+ d->m_eventTree = new QmlProfilerEventsMainView(this, d->modelProxy);
+ connect(d->m_eventTree, SIGNAL(gotoSourceLocation(QString,int,int)), this, SIGNAL(gotoSourceLocation(QString,int,int)));
+ connect(d->m_eventTree, SIGNAL(showEventInTimeline(int)), this, SIGNAL(showEventInTimeline(int)));
+
+ d->m_eventChildren = new QmlProfilerEventRelativesView(
+ profilerModelManager,
+ new QmlProfilerEventChildrenModelProxy(profilerModelManager, d->modelProxy, this),
+ this);
+ d->m_eventParents = new QmlProfilerEventRelativesView(
+ profilerModelManager,
+ new QmlProfilerEventParentsModelProxy(profilerModelManager, d->modelProxy, this),
+ this);
+ connect(d->m_eventTree, SIGNAL(eventSelected(QString)), d->m_eventChildren, SLOT(displayEvent(QString)));
+ connect(d->m_eventTree, SIGNAL(eventSelected(QString)), d->m_eventParents, SLOT(displayEvent(QString)));
+ connect(d->m_eventChildren, SIGNAL(eventClicked(QString)), d->m_eventTree, SLOT(selectEvent(QString)));
+ connect(d->m_eventParents, SIGNAL(eventClicked(QString)), d->m_eventTree, SLOT(selectEvent(QString)));
+
+ // widget arrangement
+ QVBoxLayout *groupLayout = new QVBoxLayout;
+ groupLayout->setContentsMargins(0,0,0,0);
+ groupLayout->setSpacing(0);
+
+ Core::MiniSplitter *splitterVertical = new Core::MiniSplitter;
+ splitterVertical->addWidget(d->m_eventTree);
+ Core::MiniSplitter *splitterHorizontal = new Core::MiniSplitter;
+ splitterHorizontal->addWidget(d->m_eventParents);
+ splitterHorizontal->addWidget(d->m_eventChildren);
+ splitterHorizontal->setOrientation(Qt::Horizontal);
+ splitterVertical->addWidget(splitterHorizontal);
+ splitterVertical->setOrientation(Qt::Vertical);
+ splitterVertical->setStretchFactor(0,5);
+ splitterVertical->setStretchFactor(1,2);
+ groupLayout->addWidget(splitterVertical);
+ setLayout(groupLayout);
+
+ d->m_profilerTool = profilerTool;
+ d->m_viewContainer = container;
+ d->globalStats = true;
+}
+
+QmlProfilerEventsWidget::~QmlProfilerEventsWidget()
+{
+ delete d->modelProxy;
+ delete d;
+}
+
+void QmlProfilerEventsWidget::profilerDataModelStateChanged()
+{
+}
+
+void QmlProfilerEventsWidget::clear()
+{
+ d->m_eventTree->clear();
+ d->m_eventChildren->clear();
+ d->m_eventParents->clear();
+}
+
+void QmlProfilerEventsWidget::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
+{
+ d->modelProxy->limitToRange(rangeStart, rangeEnd);
+ d->globalStats = (rangeStart == -1) && (rangeEnd == -1);
+}
+
+QModelIndex QmlProfilerEventsWidget::selectedItem() const
+{
+ return d->m_eventTree->selectedItem();
+}
+
+void QmlProfilerEventsWidget::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QTC_ASSERT(d->m_viewContainer, return;);
+
+ QMenu menu;
+ QAction *copyRowAction = 0;
+ QAction *copyTableAction = 0;
+ QAction *showExtendedStatsAction = 0;
+ QAction *getLocalStatsAction = 0;
+ QAction *getGlobalStatsAction = 0;
+
+ QmlProfilerTool *profilerTool = qobject_cast<QmlProfilerTool *>(d->m_profilerTool);
+ QPoint position = ev->globalPos();
+
+ if (profilerTool) {
+ QList <QAction *> commonActions = profilerTool->profilerContextMenuActions();
+ foreach (QAction *act, commonActions) {
+ menu.addAction(act);
+ }
+ }
+
+ if (mouseOnTable(position)) {
+ menu.addSeparator();
+ if (selectedItem().isValid())
+ copyRowAction = menu.addAction(tr("Copy Row"));
+ copyTableAction = menu.addAction(tr("Copy Table"));
+
+ showExtendedStatsAction = menu.addAction(tr("Extended Event Statistics"));
+ showExtendedStatsAction->setCheckable(true);
+ showExtendedStatsAction->setChecked(showExtendedStatistics());
+ }
+
+ menu.addSeparator();
+ getLocalStatsAction = menu.addAction(tr("Limit Events Pane to Current Range"));
+ if (!d->m_viewContainer->hasValidSelection())
+ getLocalStatsAction->setEnabled(false);
+ getGlobalStatsAction = menu.addAction(tr("Reset Events Pane"));
+ if (hasGlobalStats())
+ getGlobalStatsAction->setEnabled(false);
+
+ QAction *selectedAction = menu.exec(position);
+
+ if (selectedAction) {
+ if (selectedAction == copyRowAction)
+ copyRowToClipboard();
+ if (selectedAction == copyTableAction)
+ copyTableToClipboard();
+ if (selectedAction == getLocalStatsAction) {
+ getStatisticsInRange(d->m_viewContainer->selectionStart(),
+ d->m_viewContainer->selectionEnd());
+ }
+ if (selectedAction == getGlobalStatsAction)
+ getStatisticsInRange(-1, -1);
+ if (selectedAction == showExtendedStatsAction)
+ setShowExtendedStatistics(!showExtendedStatistics());
+ }
+}
+
+void QmlProfilerEventsWidget::resizeEvent(QResizeEvent *event)
+{
+ QWidget::resizeEvent(event);
+ emit resized();
+}
+
+bool QmlProfilerEventsWidget::mouseOnTable(const QPoint &position) const
+{
+ QPoint tableTopLeft = d->m_eventTree->mapToGlobal(QPoint(0,0));
+ QPoint tableBottomRight = d->m_eventTree->mapToGlobal(QPoint(d->m_eventTree->width(), d->m_eventTree->height()));
+ return (position.x() >= tableTopLeft.x() && position.x() <= tableBottomRight.x() && position.y() >= tableTopLeft.y() && position.y() <= tableBottomRight.y());
+}
+
+void QmlProfilerEventsWidget::copyTableToClipboard() const
+{
+ d->m_eventTree->copyTableToClipboard();
+}
+
+void QmlProfilerEventsWidget::copyRowToClipboard() const
+{
+ d->m_eventTree->copyRowToClipboard();
+}
+
+void QmlProfilerEventsWidget::updateSelectedEvent(const QString &eventHash) const
+{
+ if (d->m_eventTree->selectedEventHash() != eventHash)
+ d->m_eventTree->selectEvent(eventHash);
+}
+
+void QmlProfilerEventsWidget::selectBySourceLocation(const QString &filename, int line, int column)
+{
+ // This slot is used to connect the javascript pane with the qml events pane
+ // Our javascript trace data does not store column information
+ // thus we ignore it here
+ Q_UNUSED(column);
+ d->m_eventTree->selectEventByLocation(filename, line);
+}
+
+bool QmlProfilerEventsWidget::hasGlobalStats() const
+{
+ return d->globalStats;
+}
+
+void QmlProfilerEventsWidget::setShowExtendedStatistics(bool show)
+{
+ d->m_eventTree->setShowExtendedStatistics(show);
+}
+
+bool QmlProfilerEventsWidget::showExtendedStatistics() const
+{
+ return d->m_eventTree->showExtendedStatistics();
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate
+{
+public:
+ QmlProfilerEventsMainViewPrivate(QmlProfilerEventsMainView *qq) : q(qq) {}
+
+ int getFieldCount();
+
+ QString textForItem(QStandardItem *item, bool recursive = false) const;
+
+
+ QmlProfilerEventsMainView *q;
+
+ QmlProfilerEventsModelProxy *modelProxy;
+ QStandardItemModel *m_model;
+ QList<bool> m_fieldShown;
+ QHash<int, int> m_columnIndex; // maps field enum to column index
+ bool m_showExtendedStatistics;
+ int m_firstNumericColumn;
+ bool m_preventSelectBounce;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+QmlProfilerEventsMainView::QmlProfilerEventsMainView(QWidget *parent,
+ QmlProfilerEventsModelProxy *modelProxy)
+: QmlProfilerTreeView(parent), d(new QmlProfilerEventsMainViewPrivate(this))
+{
+ setObjectName(QLatin1String("QmlProfilerEventsTable"));
+
+ setSortingEnabled(false);
+
+ d->m_model = new QStandardItemModel(this);
+ setModel(d->m_model);
+ connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
+
+ d->modelProxy = modelProxy;
+ connect(d->modelProxy,SIGNAL(dataAvailable()), this, SLOT(buildModel()));
+// connect(d->modelProxy,SIGNAL(stateChanged()),
+// this,SLOT(profilerDataModelStateChanged()));
+ d->m_firstNumericColumn = 0;
+ d->m_preventSelectBounce = false;
+ d->m_showExtendedStatistics = false;
+
+ setFieldViewable(Name, true);
+ setFieldViewable(Type, true);
+ setFieldViewable(Percent, true);
+ setFieldViewable(TotalDuration, true);
+ setFieldViewable(SelfPercent, false);
+ setFieldViewable(SelfDuration, false);
+ setFieldViewable(CallCount, true);
+ setFieldViewable(TimePerCall, true);
+ setFieldViewable(MaxTime, true);
+ setFieldViewable(MinTime, true);
+ setFieldViewable(MedianTime, true);
+ setFieldViewable(Details, true);
+
+ buildModel();
+}
+
+QmlProfilerEventsMainView::~QmlProfilerEventsMainView()
+{
+ clear();
+ //delete d->modelProxy;
+ delete d->m_model;
+ delete d;
+}
+
+void QmlProfilerEventsMainView::profilerDataModelStateChanged()
+{
+}
+
+void QmlProfilerEventsMainView::setFieldViewable(Fields field, bool show)
+{
+ if (field < MaxFields) {
+ int length = d->m_fieldShown.count();
+ if (field >= length) {
+ for (int i=length; i<MaxFields; i++)
+ d->m_fieldShown << false;
+ }
+ d->m_fieldShown[field] = show;
+ }
+}
+
+
+void QmlProfilerEventsMainView::setHeaderLabels()
+{
+ int fieldIndex = 0;
+ d->m_firstNumericColumn = 0;
+
+ d->m_columnIndex.clear();
+ if (d->m_fieldShown[Name]) {
+ d->m_columnIndex[Name] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Location")));
+ d->m_firstNumericColumn++;
+ }
+ if (d->m_fieldShown[Type]) {
+ d->m_columnIndex[Type] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Type")));
+ d->m_firstNumericColumn++;
+ }
+ if (d->m_fieldShown[Percent]) {
+ d->m_columnIndex[Percent] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Time in Percent")));
+ }
+ if (d->m_fieldShown[TotalDuration]) {
+ d->m_columnIndex[TotalDuration] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Total Time")));
+ }
+ if (d->m_fieldShown[SelfPercent]) {
+ d->m_columnIndex[Type] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Self Time in Percent")));
+ }
+ if (d->m_fieldShown[SelfDuration]) {
+ d->m_columnIndex[SelfDuration] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Self Time")));
+ }
+ if (d->m_fieldShown[CallCount]) {
+ d->m_columnIndex[CallCount] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Calls")));
+ }
+ if (d->m_fieldShown[TimePerCall]) {
+ d->m_columnIndex[TimePerCall] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Mean Time")));
+ }
+ if (d->m_fieldShown[MedianTime]) {
+ d->m_columnIndex[MedianTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Median Time")));
+ }
+ if (d->m_fieldShown[MaxTime]) {
+ d->m_columnIndex[MaxTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Longest Time")));
+ }
+ if (d->m_fieldShown[MinTime]) {
+ d->m_columnIndex[MinTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Shortest Time")));
+ }
+ if (d->m_fieldShown[Details]) {
+ d->m_columnIndex[Details] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Details")));
+ }
+}
+
+void QmlProfilerEventsMainView::setShowExtendedStatistics(bool show)
+{
+ // Not checking if already set because we don't want the first call to skip
+ d->m_showExtendedStatistics = show;
+ if (show) {
+ if (d->m_fieldShown[MedianTime])
+ showColumn(d->m_columnIndex[MedianTime]);
+ if (d->m_fieldShown[MaxTime])
+ showColumn(d->m_columnIndex[MaxTime]);
+ if (d->m_fieldShown[MinTime])
+ showColumn(d->m_columnIndex[MinTime]);
+ } else{
+ if (d->m_fieldShown[MedianTime])
+ hideColumn(d->m_columnIndex[MedianTime]);
+ if (d->m_fieldShown[MaxTime])
+ hideColumn(d->m_columnIndex[MaxTime]);
+ if (d->m_fieldShown[MinTime])
+ hideColumn(d->m_columnIndex[MinTime]);
+ }
+}
+
+bool QmlProfilerEventsMainView::showExtendedStatistics() const
+{
+ return d->m_showExtendedStatistics;
+}
+
+void QmlProfilerEventsMainView::clear()
+{
+ d->m_model->clear();
+ d->m_model->setColumnCount(d->getFieldCount());
+
+ setHeaderLabels();
+ setSortingEnabled(false);
+}
+
+int QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::getFieldCount()
+{
+ int count = 0;
+ for (int i=0; i < m_fieldShown.count(); ++i)
+ if (m_fieldShown[i])
+ count++;
+ return count;
+}
+
+void QmlProfilerEventsMainView::buildModel()
+{
+ clear();
+ parseModelProxy();
+ setShowExtendedStatistics(d->m_showExtendedStatistics);
+
+ setRootIsDecorated(false);
+ setSortingEnabled(true);
+ sortByColumn(d->m_firstNumericColumn,Qt::DescendingOrder);
+
+ expandAll();
+ if (d->m_fieldShown[Name])
+ resizeColumnToContents(0);
+
+ if (d->m_fieldShown[Type])
+ resizeColumnToContents(d->m_fieldShown[Name]?1:0);
+ collapseAll();
+}
+
+void QmlProfilerEventsMainView::parseModelProxy()
+{
+ const QList <QmlProfilerEventsModelProxy::QmlEventStats> eventList = d->modelProxy->getData();
+ foreach (const QmlProfilerEventsModelProxy::QmlEventStats &event, eventList) {
+ QStandardItem *parentItem = d->m_model->invisibleRootItem();
+ QList<QStandardItem *> newRow;
+
+ if (d->m_fieldShown[Name])
+ newRow << new EventsViewItem(event.displayName);
+
+ if (d->m_fieldShown[Type]) {
+ QString typeString = QmlProfilerEventsMainView::nameForType(event.eventType);
+ QString toolTipText;
+ if (event.eventType == Binding) {
+ if (event.bindingType == (int)OptimizedBinding) {
+ typeString = typeString + tr(" (Opt)");
+ toolTipText = tr("Binding is evaluated by the optimized engine.");
+ } else if (event.bindingType == (int)V8Binding) {
+ toolTipText = tr("Binding not optimized (e.g. has side effects or assignments,\n"
+ "references to elements in other files, loops, etc.)");
+
+ }
+ }
+ newRow << new EventsViewItem(typeString);
+ newRow.last()->setData(QVariant(typeString));
+ if (!toolTipText.isEmpty())
+ newRow.last()->setToolTip(toolTipText);
+ }
+
+ if (d->m_fieldShown[Percent]) {
+ newRow << new EventsViewItem(QString::number(event.percentOfTime,'f',2)+QLatin1String(" %"));
+ newRow.last()->setData(QVariant(event.percentOfTime));
+ }
+
+ if (d->m_fieldShown[TotalDuration]) {
+ newRow << new EventsViewItem(displayTime(event.duration));
+ newRow.last()->setData(QVariant(event.duration));
+ }
+
+ if (d->m_fieldShown[CallCount]) {
+ newRow << new EventsViewItem(QString::number(event.calls));
+ newRow.last()->setData(QVariant(event.calls));
+ }
+
+ if (d->m_fieldShown[TimePerCall]) {
+ newRow << new EventsViewItem(displayTime(event.timePerCall));
+ newRow.last()->setData(QVariant(event.timePerCall));
+ }
+
+ if (d->m_fieldShown[MedianTime]) {
+ newRow << new EventsViewItem(displayTime(event.medianTime));
+ newRow.last()->setData(QVariant(event.medianTime));
+ }
+
+ if (d->m_fieldShown[MaxTime]) {
+ newRow << new EventsViewItem(displayTime(event.maxTime));
+ newRow.last()->setData(QVariant(event.maxTime));
+ }
+
+ if (d->m_fieldShown[MinTime]) {
+ newRow << new EventsViewItem(displayTime(event.minTime));
+ newRow.last()->setData(QVariant(event.minTime));
+ }
+
+ if (d->m_fieldShown[Details]) {
+ newRow << new EventsViewItem(event.details);
+ newRow.last()->setData(QVariant(event.details));
+ }
+
+
+
+ if (!newRow.isEmpty()) {
+ // no edit
+ foreach (QStandardItem *item, newRow)
+ item->setEditable(false);
+
+ // metadata
+ newRow.at(0)->setData(QVariant(event.eventHashStr),EventHashStrRole);
+ newRow.at(0)->setData(QVariant(event.location.filename),FilenameRole);
+ newRow.at(0)->setData(QVariant(event.location.line),LineRole);
+ newRow.at(0)->setData(QVariant(event.location.column),ColumnRole);
+
+ if (event.isBindingLoop) {
+ foreach (QStandardItem *item, newRow) {
+ item->setBackground(colors()->bindingLoopBackground);
+ item->setToolTip(tr("Binding loop detected."));
+ }
+ }
+
+ // append
+ parentItem->appendRow(newRow);
+ }
+ }
+}
+
+QString QmlProfilerEventsMainView::displayTime(double time)
+{
+ if (time < 1e6)
+ return QString::number(time/1e3,'f',3) + trUtf8(" \xc2\xb5s");
+ if (time < 1e9)
+ return QString::number(time/1e6,'f',3) + tr(" ms");
+
+ return QString::number(time/1e9,'f',3) + tr(" s");
+}
+
+QString QmlProfilerEventsMainView::nameForType(int typeNumber)
+{
+ switch (typeNumber) {
+ case 0: return QmlProfilerEventsMainView::tr("Paint");
+ case 1: return QmlProfilerEventsMainView::tr("Compile");
+ case 2: return QmlProfilerEventsMainView::tr("Create");
+ case 3: return QmlProfilerEventsMainView::tr("Binding");
+ case 4: return QmlProfilerEventsMainView::tr("Signal");
+ }
+ return QString();
+}
+
+void QmlProfilerEventsMainView::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
+{
+ d->modelProxy->limitToRange(rangeStart, rangeEnd);
+}
+
+QString QmlProfilerEventsMainView::selectedEventHash() const
+{
+ QModelIndex index = selectedItem();
+ if (!index.isValid())
+ return QString();
+ QStandardItem *item = d->m_model->item(index.row(), 0);
+ return item->data(EventHashStrRole).toString();
+}
+
+
+void QmlProfilerEventsMainView::jumpToItem(const QModelIndex &index)
+{
+ if (d->m_preventSelectBounce)
+ return;
+
+ d->m_preventSelectBounce = true;
+ QStandardItem *clickedItem = d->m_model->itemFromIndex(index);
+ QStandardItem *infoItem;
+ if (clickedItem->parent())
+ infoItem = clickedItem->parent()->child(clickedItem->row(), 0);
+ else
+ infoItem = d->m_model->item(index.row(), 0);
+
+ // show in editor
+ int line = infoItem->data(LineRole).toInt();
+ int column = infoItem->data(ColumnRole).toInt();
+ QString fileName = infoItem->data(FilenameRole).toString();
+ if (line!=-1 && !fileName.isEmpty())
+ emit gotoSourceLocation(fileName, line, column);
+
+ // show in callers/callees subwindow
+ emit eventSelected(infoItem->data(EventHashStrRole).toString());
+
+ // show in timelinerenderer
+ emit showEventInTimeline(infoItem->data(EventIdRole).toInt());
+
+ d->m_preventSelectBounce = false;
+}
+
+void QmlProfilerEventsMainView::selectEvent(const QString &eventHash)
+{
+ for (int i=0; i<d->m_model->rowCount(); i++) {
+ QStandardItem *infoItem = d->m_model->item(i, 0);
+ if (infoItem->data(EventHashStrRole).toString() == eventHash) {
+ setCurrentIndex(d->m_model->indexFromItem(infoItem));
+ jumpToItem(currentIndex());
+ return;
+ }
+ }
+}
+
+void QmlProfilerEventsMainView::selectEventByLocation(const QString &filename, int line)
+{
+ if (d->m_preventSelectBounce)
+ return;
+
+ for (int i=0; i<d->m_model->rowCount(); i++) {
+ QStandardItem *infoItem = d->m_model->item(i, 0);
+ if (currentIndex() != d->m_model->indexFromItem(infoItem) && infoItem->data(FilenameRole).toString() == filename && infoItem->data(LineRole).toInt() == line) {
+ setCurrentIndex(d->m_model->indexFromItem(infoItem));
+ jumpToItem(currentIndex());
+ return;
+ }
+ }
+}
+
+QModelIndex QmlProfilerEventsMainView::selectedItem() const
+{
+ QModelIndexList sel = selectedIndexes();
+ if (sel.isEmpty())
+ return QModelIndex();
+ else
+ return sel.first();
+}
+
+void QmlProfilerEventsMainView::changeDetailsForEvent(int eventId, const QString &newString)
+{
+ for (int i=0; i<d->m_model->rowCount(); i++) {
+ QStandardItem *infoItem = d->m_model->item(i, 0);
+ if (infoItem->data(EventIdRole).toInt() == eventId) {
+ d->m_model->item(i,d->m_columnIndex[Details])->setData(QVariant(newString),Qt::DisplayRole);
+ d->m_model->item(i,d->m_columnIndex[Details])->setData(QVariant(newString));
+ return;
+ }
+ }
+}
+
+QString QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::textForItem(QStandardItem *item, bool recursive) const
+{
+ QString str;
+
+ if (recursive) {
+ // indentation
+ QStandardItem *itemParent = item->parent();
+ while (itemParent) {
+ str += QLatin1String(" ");
+ itemParent = itemParent->parent();
+ }
+ }
+
+ // item's data
+ int colCount = m_model->columnCount();
+ for (int j = 0; j < colCount; ++j) {
+ QStandardItem *colItem = item->parent() ? item->parent()->child(item->row(),j) : m_model->item(item->row(),j);
+ str += colItem->data(Qt::DisplayRole).toString();
+ if (j < colCount-1) str += QLatin1Char('\t');
+ }
+ str += QLatin1Char('\n');
+
+ // recursively print children
+ if (recursive && item->child(0))
+ for (int j = 0; j != item->rowCount(); j++)
+ str += textForItem(item->child(j));
+
+ return str;
+}
+
+void QmlProfilerEventsMainView::copyTableToClipboard() const
+{
+ QString str;
+ // headers
+ int columnCount = d->m_model->columnCount();
+ for (int i = 0; i < columnCount; ++i) {
+ str += d->m_model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
+ if (i < columnCount - 1)
+ str += QLatin1Char('\t');
+ else
+ str += QLatin1Char('\n');
+ }
+ // data
+ int rowCount = d->m_model->rowCount();
+ for (int i = 0; i != rowCount; ++i) {
+ str += d->textForItem(d->m_model->item(i));
+ }
+ QClipboard *clipboard = QApplication::clipboard();
+ clipboard->setText(str, QClipboard::Selection);
+ clipboard->setText(str, QClipboard::Clipboard);
+}
+
+void QmlProfilerEventsMainView::copyRowToClipboard() const
+{
+ QString str;
+ str = d->textForItem(d->m_model->itemFromIndex(selectedItem()), false);
+
+ QClipboard *clipboard = QApplication::clipboard();
+ clipboard->setText(str, QClipboard::Selection);
+ clipboard->setText(str, QClipboard::Clipboard);
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class QmlProfilerEventRelativesView::QmlProfilerEventParentsViewPrivate
+{
+public:
+ QmlProfilerEventParentsViewPrivate(QmlProfilerEventRelativesView *qq):q(qq) {}
+ ~QmlProfilerEventParentsViewPrivate() {}
+
+ QmlProfilerEventRelativesModelProxy *modelProxy;
+
+ QmlProfilerEventRelativesView *q;
+};
+
+QmlProfilerEventRelativesView::QmlProfilerEventRelativesView(QmlProfilerModelManager *modelManager, QmlProfilerEventRelativesModelProxy *modelProxy, QWidget *parent)
+ : QmlProfilerTreeView(parent), d(new QmlProfilerEventParentsViewPrivate(this))
+{
+ Q_UNUSED(modelManager);
+ setSortingEnabled(false);
+ d->modelProxy = modelProxy;
+ setModel(new QStandardItemModel(this));
+ setRootIsDecorated(false);
+ updateHeader();
+
+ connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
+}
+
+QmlProfilerEventRelativesView::~QmlProfilerEventRelativesView()
+{
+ delete d;
+}
+
+void QmlProfilerEventRelativesView::displayEvent(const QString &eventHash)
+{
+ rebuildTree(d->modelProxy->getData(eventHash));
+
+ updateHeader();
+ resizeColumnToContents(0);
+ setSortingEnabled(true);
+ sortByColumn(2);
+}
+
+void QmlProfilerEventRelativesView::rebuildTree(QmlProfilerEventRelativesModelProxy::QmlEventRelativesMap eventMap)
+{
+ Q_ASSERT(treeModel());
+ treeModel()->clear();
+
+ QStandardItem *topLevelItem = treeModel()->invisibleRootItem();
+
+ //foreach (const QmlProfilerEventParentsModelProxy::QmlEventParentData &event, eventMap.values()) {
+ foreach (const QString &key, eventMap.keys()) {
+ const QmlProfilerEventRelativesModelProxy::QmlEventRelativesData &event = eventMap[key];
+ QList<QStandardItem *> newRow;
+
+ // ToDo: here we were going to search for the data in the other modelproxy
+ // maybe we should store the data in this proxy and get it here
+ // no indirections at this level of abstraction!
+ newRow << new EventsViewItem(event.displayName);
+ newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event.eventType));
+ newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event.duration));
+ newRow << new EventsViewItem(QString::number(event.calls));
+ newRow << new EventsViewItem(event.details);
+
+// newRow << new EventsViewItem(event->reference->displayName);
+// newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event->reference->eventType));
+// newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event->duration));
+// newRow << new EventsViewItem(QString::number(event->calls));
+// newRow << new EventsViewItem(event->reference->details);
+ newRow.at(0)->setData(QVariant(key), EventHashStrRole);
+// newRow.at(0)->setData(QVariant(event->reference->eventId), EventIdRole);
+ newRow.at(2)->setData(QVariant(event.duration));
+ newRow.at(3)->setData(QVariant(event.calls));
+
+ if (event.isBindingLoop) {
+ foreach (QStandardItem *item, newRow) {
+ item->setBackground(colors()->bindingLoopBackground);
+ item->setToolTip(tr("Part of binding loop."));
+ }
+ }
+
+ foreach (QStandardItem *item, newRow)
+ item->setEditable(false);
+
+ topLevelItem->appendRow(newRow);
+ }
+}
+
+void QmlProfilerEventRelativesView::clear()
+{
+ if (treeModel()) {
+ treeModel()->clear();
+ updateHeader();
+ }
+}
+
+void QmlProfilerEventRelativesView::updateHeader()
+{
+ bool calleesView = qobject_cast<QmlProfilerEventChildrenModelProxy *>(d->modelProxy) != 0;
+
+ if (treeModel()) {
+ treeModel()->setColumnCount(5);
+
+ int columnIndex = 0;
+ if (calleesView)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee")));
+ else
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller")));
+
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Type")));
+
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Total Time")));
+
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Calls")));
+
+
+ if (calleesView)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee Description")));
+ else
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller Description")));
+ }
+}
+
+QStandardItemModel *QmlProfilerEventRelativesView::treeModel()
+{
+ return qobject_cast<QStandardItemModel *>(model());
+}
+
+void QmlProfilerEventRelativesView::jumpToItem(const QModelIndex &index)
+{
+ if (treeModel()) {
+ QStandardItem *infoItem = treeModel()->item(index.row(), 0);
+ emit eventClicked(infoItem->data(EventHashStrRole).toString());
+ }
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/plugins/qmlprofiler/qmlprofilereventview.h b/plugins/qmlprofiler/qmlprofilereventview.h
new file mode 100644
index 0000000000..8294a5f30e
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilereventview.h
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILEREVENTVIEW_H
+#define QMLPROFILEREVENTVIEW_H
+
+#include <QTreeView>
+#include <QStandardItemModel>
+#include <qmldebug/qmlprofilereventtypes.h>
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilereventsmodelproxy.h"
+#include "qmlprofilertreeview.h"
+
+#include <analyzerbase/ianalyzertool.h>
+
+#include "qmlprofilerviewmanager.h"
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerEventsMainView;
+class QmlProfilerEventChildrenView;
+class QmlProfilerEventRelativesView;
+
+enum ItemRole {
+ EventHashStrRole = Qt::UserRole+1,
+ FilenameRole = Qt::UserRole+2,
+ LineRole = Qt::UserRole+3,
+ ColumnRole = Qt::UserRole+4,
+ EventIdRole = Qt::UserRole+5
+};
+
+class QmlProfilerEventsWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit QmlProfilerEventsWidget(QWidget *parent,
+ Analyzer::IAnalyzerTool *profilerTool,
+ QmlProfilerViewManager *container,
+ QmlProfilerModelManager *profilerModelManager );
+ ~QmlProfilerEventsWidget();
+
+ void clear();
+
+ void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
+ QModelIndex selectedItem() const;
+ bool mouseOnTable(const QPoint &position) const;
+ void copyTableToClipboard() const;
+ void copyRowToClipboard() const;
+
+ bool hasGlobalStats() const;
+ void setShowExtendedStatistics(bool show);
+ bool showExtendedStatistics() const;
+
+
+signals:
+ void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
+ void showEventInTimeline(int eventId);
+ void resized();
+
+public slots:
+ void updateSelectedEvent(const QString &eventHash) const;
+ void selectBySourceLocation(const QString &filename, int line, int column);
+
+private slots:
+ void profilerDataModelStateChanged();
+
+protected:
+ void contextMenuEvent(QContextMenuEvent *ev);
+ virtual void resizeEvent(QResizeEvent *event);
+
+private:
+ class QmlProfilerEventsWidgetPrivate;
+ QmlProfilerEventsWidgetPrivate *d;
+};
+
+class QmlProfilerEventsMainView : public QmlProfilerTreeView
+{
+ Q_OBJECT
+public:
+ enum Fields {
+ Name,
+ Type,
+ Percent,
+ TotalDuration,
+ SelfPercent,
+ SelfDuration,
+ CallCount,
+ TimePerCall,
+ MaxTime,
+ MinTime,
+ MedianTime,
+ Details,
+
+ MaxFields
+ };
+
+ explicit QmlProfilerEventsMainView(QWidget *parent,
+ QmlProfilerEventsModelProxy *modelProxy);
+ ~QmlProfilerEventsMainView();
+
+ void setFieldViewable(Fields field, bool show);
+ void setShowAnonymousEvents( bool showThem );
+
+ QModelIndex selectedItem() const;
+ void copyTableToClipboard() const;
+ void copyRowToClipboard() const;
+
+ static QString displayTime(double time);
+ static QString nameForType(int typeNumber);
+
+ void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
+// int selectedEventId() const;
+ QString selectedEventHash() const;
+
+ void setShowExtendedStatistics(bool);
+ bool showExtendedStatistics() const;
+
+
+signals:
+ void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
+ void eventSelected(const QString &eventHash);
+ void showEventInTimeline(int eventId);
+
+public slots:
+ void clear();
+ void jumpToItem(const QModelIndex &index);
+ void selectEvent(const QString &eventHash);
+ void selectEventByLocation(const QString &filename, int line);
+ void buildModel();
+ void changeDetailsForEvent(int eventId, const QString &newString);
+
+private slots:
+ void profilerDataModelStateChanged();
+
+private:
+ void setHeaderLabels();
+ void parseModelProxy();
+
+private:
+ class QmlProfilerEventsMainViewPrivate;
+ QmlProfilerEventsMainViewPrivate *d;
+
+};
+
+class QmlProfilerEventRelativesView : public QmlProfilerTreeView
+{
+ Q_OBJECT
+public:
+ explicit QmlProfilerEventRelativesView(QmlProfilerModelManager *modelManager,
+ QmlProfilerEventRelativesModelProxy *modelProxy,
+ QWidget *parent );
+ ~QmlProfilerEventRelativesView();
+
+signals:
+ void eventClicked(const QString &eventHash);
+
+public slots:
+ void displayEvent(const QString &eventHash);
+ void jumpToItem(const QModelIndex &);
+ void clear();
+
+private:
+ void rebuildTree(QmlProfilerEventParentsModelProxy::QmlEventRelativesMap eventMap);
+ void updateHeader();
+ QStandardItemModel *treeModel();
+// QmlProfilerModelManager *m_profilerModelManager;
+
+ class QmlProfilerEventParentsViewPrivate;
+ QmlProfilerEventParentsViewPrivate *d;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILEREVENTVIEW_H
diff --git a/plugins/qmlprofiler/qmlprofilermodelmanager.cpp b/plugins/qmlprofiler/qmlprofilermodelmanager.cpp
new file mode 100644
index 0000000000..c8343c4dd4
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilermodelmanager.cpp
@@ -0,0 +1,309 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilersimplemodel.h"
+#include "qmlprofilerprocessedmodel.h"
+#include "qv8profilerdatamodel.h"
+#include "qmlprofilertracefile.h"
+
+#include <utils/qtcassert.h>
+
+#include <QDebug>
+#include <QFile>
+
+namespace QmlProfiler {
+namespace Internal {
+
+
+/////////////////////////////////////////////////////////////////////
+QmlProfilerDataState::QmlProfilerDataState(QmlProfilerModelManager *modelManager, QObject *parent)
+ : QObject(parent), m_state(Empty), m_modelManager(modelManager)
+{
+ connect(this, SIGNAL(error(QString)), m_modelManager, SIGNAL(error(QString)));
+ connect(this, SIGNAL(stateChanged()), m_modelManager, SIGNAL(stateChanged()));
+}
+
+void QmlProfilerDataState::setState(QmlProfilerDataState::State state)
+{
+ // It's not an error, we are continuously calling "AcquiringData" for example
+ if (m_state == state)
+ return;
+
+ switch (state) {
+ case Empty:
+ // if it's not empty, complain but go on
+ QTC_ASSERT(m_modelManager->isEmpty(), /**/);
+ break;
+ case AcquiringData:
+ // we're not supposed to receive new data while processing older data
+ QTC_ASSERT(m_state != ProcessingData, return);
+ break;
+ case ProcessingData:
+ QTC_ASSERT(m_state == AcquiringData, return);
+ break;
+ case Done:
+ QTC_ASSERT(m_state == ProcessingData || m_state == Empty, return);
+ break;
+ default:
+ emit error(tr("Trying to set unknown state in events list"));
+ break;
+ }
+
+ m_state = state;
+ emit stateChanged();
+
+ return;
+}
+
+
+/////////////////////////////////////////////////////////////////////
+QmlProfilerTraceTime::QmlProfilerTraceTime(QObject *parent) : QObject(parent)
+{
+ clear();
+}
+
+QmlProfilerTraceTime::~QmlProfilerTraceTime()
+{
+}
+
+qint64 QmlProfilerTraceTime::startTime() const
+{
+ return m_startTime;
+}
+
+qint64 QmlProfilerTraceTime::endTime() const
+{
+ return m_endTime;
+}
+
+qint64 QmlProfilerTraceTime::duration() const
+{
+ return endTime() - startTime();
+}
+
+void QmlProfilerTraceTime::clear()
+{
+ m_startTime = -1;
+ m_endTime = 0;
+}
+
+void QmlProfilerTraceTime::setStartTime(qint64 time)
+{
+ m_startTime = time;
+}
+
+void QmlProfilerTraceTime::setEndTime(qint64 time)
+{
+ m_endTime = time;
+}
+
+
+/////////////////////////////////////////////////////////////////////
+
+class QmlProfilerModelManager::QmlProfilerModelManagerPrivate
+{
+public:
+ QmlProfilerModelManagerPrivate(QmlProfilerModelManager *qq) : q(qq) {}
+ ~QmlProfilerModelManagerPrivate() {}
+ QmlProfilerModelManager *q;
+
+ QmlProfilerSimpleModel *model;
+ QV8ProfilerDataModel *v8Model;
+ QmlProfilerDataState *dataState;
+ QmlProfilerTraceTime *traceTime;
+
+ // file to load
+ QString fileName;
+};
+
+
+QmlProfilerModelManager::QmlProfilerModelManager(Utils::FileInProjectFinder *finder, QObject *parent) :
+ QObject(parent), d(new QmlProfilerModelManagerPrivate(this))
+{
+ d->model = new QmlProfilerProcessedModel(finder, this);
+ d->v8Model = new QV8ProfilerDataModel(this);
+// d->model = new QmlProfilerSimpleModel(this);
+ d->dataState = new QmlProfilerDataState(this, this);
+ d->traceTime = new QmlProfilerTraceTime(this);
+}
+
+QmlProfilerModelManager::~QmlProfilerModelManager()
+{
+ delete d;
+}
+
+QmlProfilerTraceTime *QmlProfilerModelManager::traceTime() const
+{
+ return d->traceTime;
+}
+
+// TODO: rename to just "model" or something like that
+QmlProfilerSimpleModel *QmlProfilerModelManager::simpleModel() const
+{
+ return d->model;
+}
+
+QV8ProfilerDataModel *QmlProfilerModelManager::v8Model() const
+{
+ return d->v8Model;
+}
+
+bool QmlProfilerModelManager::isEmpty() const
+{
+ return d->model->isEmpty() && d->v8Model->isEmpty();
+}
+
+int QmlProfilerModelManager::count() const
+{
+ return d->model->count();
+}
+
+void QmlProfilerModelManager::addRangedEvent(int type, int bindingType, qint64 startTime, qint64 length, const QStringList &data, const QmlDebug::QmlEventLocation &location)
+{
+ // If trace start time was not explicitly set, use the first event
+ if (d->traceTime->startTime() == -1)
+ d->traceTime->setStartTime(startTime);
+
+ QTC_ASSERT(state() == QmlProfilerDataState::AcquiringData, /**/);
+ d->model->addRangedEvent(type, bindingType, startTime, length, data, location);
+ emit countChanged();
+}
+
+void QmlProfilerModelManager::addV8Event(int depth, const QString &function, const QString &filename,
+ int lineNumber, double totalTime, double selfTime)
+{
+ d->v8Model->addV8Event(depth, function, filename, lineNumber,totalTime, selfTime);
+}
+
+void QmlProfilerModelManager::complete()
+{
+ if (state() == QmlProfilerDataState::AcquiringData) {
+ // If trace end time was not explicitly set, use the last event
+ if (d->traceTime->endTime() == 0)
+ d->traceTime->setEndTime(d->model->lastTimeMark());
+ setState(QmlProfilerDataState::ProcessingData);
+ d->model->complete();
+ d->v8Model->complete();
+ setState(QmlProfilerDataState::Done);
+ } else
+ if (state() == QmlProfilerDataState::Empty) {
+ setState(QmlProfilerDataState::Done);
+ } else
+ if (state() == QmlProfilerDataState::Done) {
+ // repeated Done states are ignored
+ } else {
+ emit error(tr("Unexpected complete signal in data model"));
+ }
+}
+
+void QmlProfilerModelManager::save(const QString &filename)
+{
+ QFile file(filename);
+ if (!file.open(QIODevice::WriteOnly)) {
+ emit error(tr("Could not open %1 for writing.").arg(filename));
+ return;
+ }
+
+ QmlProfilerFileWriter writer;
+
+ writer.setTraceTime(traceTime()->startTime(), traceTime()->endTime(), traceTime()->duration());
+ writer.setV8DataModel(d->v8Model);
+ writer.setQmlEvents(d->model->getEvents());
+ writer.save(&file);
+}
+
+void QmlProfilerModelManager::load(const QString &filename)
+{
+ d->fileName = filename;
+ load();
+}
+
+void QmlProfilerModelManager::setFilename(const QString &filename)
+{
+ d->fileName = filename;
+}
+
+void QmlProfilerModelManager::load()
+{
+ QString filename = d->fileName;
+
+ QFile file(filename);
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ emit error(tr("Could not open %1 for reading.").arg(filename));
+ return;
+ }
+
+ // erase current
+ clear();
+
+ setState(QmlProfilerDataState::AcquiringData);
+
+ QmlProfilerFileReader reader;
+ connect(&reader, SIGNAL(error(QString)), this, SIGNAL(error(QString)));
+ connect(&reader, SIGNAL(rangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)), this,
+ SLOT(addRangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)));
+ connect(&reader, SIGNAL(traceStartTime(qint64)), traceTime(), SLOT(setStartTime(qint64)));
+ connect(&reader, SIGNAL(traceEndTime(qint64)), traceTime(), SLOT(setEndTime(qint64)));
+
+ reader.setV8DataModel(d->v8Model);
+ reader.load(&file);
+
+ complete();
+}
+
+void QmlProfilerModelManager::setState(QmlProfilerDataState::State state)
+{
+ d->dataState->setState(state);
+}
+
+QmlProfilerDataState::State QmlProfilerModelManager::state() const
+{
+ return d->dataState->state();
+}
+
+void QmlProfilerModelManager::clear()
+{
+ d->model->clear();
+ d->v8Model->clear();
+ d->traceTime->clear();
+
+ emit countChanged();
+ setState(QmlProfilerDataState::Empty);
+}
+
+void QmlProfilerModelManager::prepareForWriting()
+{
+ setState(QmlProfilerDataState::AcquiringData);
+}
+
+
+}
+}
diff --git a/plugins/qmlprofiler/qmlprofilermodelmanager.h b/plugins/qmlprofiler/qmlprofilermodelmanager.h
new file mode 100644
index 0000000000..660787f98a
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilermodelmanager.h
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERMODELMANAGER_H
+#define QMLPROFILERMODELMANAGER_H
+
+#include <QObject>
+#include "qmldebug/qmlprofilereventlocation.h"
+#include <utils/fileinprojectfinder.h>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerSimpleModel;
+class QV8ProfilerDataModel;
+class QmlProfilerModelManager;
+class QmlProfilerDataState : public QObject
+{
+ Q_OBJECT
+public:
+ enum State {
+ Empty,
+ AcquiringData,
+ ProcessingData,
+ Done
+ };
+
+ explicit QmlProfilerDataState(QmlProfilerModelManager *modelManager, QObject *parent = 0);
+ ~QmlProfilerDataState() {}
+
+ State state() const { return m_state; }
+
+signals:
+ void stateChanged();
+ void error(const QString &error);
+
+private:
+ void setState(State state);
+ State m_state;
+ QmlProfilerModelManager *m_modelManager;
+
+ friend class QmlProfilerModelManager;
+};
+
+class QmlProfilerTraceTime : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QmlProfilerTraceTime(QObject *parent);
+ ~QmlProfilerTraceTime();
+
+ qint64 startTime() const;
+ qint64 endTime() const;
+ qint64 duration() const;
+
+public slots:
+ void clear();
+ void setStartTime(qint64 time);
+ void setEndTime(qint64 time);
+
+private:
+ qint64 m_startTime;
+ qint64 m_endTime;
+};
+
+// Interface between the Data Model and the Engine/Tool
+class QmlProfilerModelManager : public QObject
+{
+ Q_OBJECT
+public:
+
+ explicit QmlProfilerModelManager(Utils::FileInProjectFinder *finder, QObject *parent = 0);
+ ~QmlProfilerModelManager();
+
+ QmlProfilerDataState::State state() const;
+ QmlProfilerTraceTime *traceTime() const;
+ QmlProfilerSimpleModel *simpleModel() const;
+ QV8ProfilerDataModel *v8Model() const;
+
+ bool isEmpty() const;
+ int count() const;
+
+signals:
+ void countChanged();
+ void error(const QString &error);
+ void stateChanged();
+
+ void requestDetailsForLocation(int eventType, const QmlDebug::QmlEventLocation &location);
+
+public slots:
+ void clear();
+
+ void prepareForWriting();
+ void addRangedEvent(int type, int bindingType, qint64 startTime, qint64 length,
+ const QStringList &data, const QmlDebug::QmlEventLocation &location);
+ void addV8Event(int depth, const QString &function,const QString &filename, int lineNumber,
+ double totalTime, double selfTime);
+#ifdef PROFILER_FRAMEEVENTS
+/*change*/ void addFrameEvent(qint64 time, int framerate, int animationcount);
+#endif
+
+ void complete();
+
+ void save(const QString &filename);
+ void load(const QString &filename);
+ void setFilename(const QString &filename);
+ void load();
+
+private:
+ void setState(QmlProfilerDataState::State state);
+
+
+private:
+ class QmlProfilerModelManagerPrivate;
+ QmlProfilerModelManagerPrivate *d;
+};
+
+}
+}
+
+#endif
diff --git a/plugins/qmlprofiler/qmlprofileroverviewmodelproxy.cpp b/plugins/qmlprofiler/qmlprofileroverviewmodelproxy.cpp
new file mode 100644
index 0000000000..8da8ccd2d2
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofileroverviewmodelproxy.cpp
@@ -0,0 +1,227 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofileroverviewmodelproxy.h"
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilersimplemodel.h"
+
+#include <qmldebug/qmlprofilereventtypes.h>
+
+#include <QVector>
+#include <QHash>
+#include <QUrl>
+#include <QStack>
+#include <QString>
+
+#include <QDebug>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerOverviewModelProxy::QmlProfilerOverviewModelProxyPrivate
+{
+public:
+ QmlProfilerOverviewModelProxyPrivate(QmlProfilerOverviewModelProxy *qq) : q(qq) {}
+ ~QmlProfilerOverviewModelProxyPrivate() {}
+
+ QVector <QmlProfilerOverviewModelProxy::QmlOverviewEvent> data;
+ int rowCount;
+
+ QmlProfilerModelManager *modelManager;
+ QmlProfilerOverviewModelProxy *q;
+};
+
+QmlProfilerOverviewModelProxy::QmlProfilerOverviewModelProxy(QmlProfilerModelManager *modelManager, QObject *parent)
+ : QObject(parent), d(new QmlProfilerOverviewModelProxyPrivate(this))
+{
+ d->modelManager = modelManager;
+ connect(modelManager->simpleModel(),SIGNAL(changed()),this,SLOT(dataChanged()));
+ connect(modelManager,SIGNAL(countChanged()),this,SIGNAL(countChanged()));
+}
+
+QmlProfilerOverviewModelProxy::~QmlProfilerOverviewModelProxy()
+{
+ delete d;
+}
+
+const QVector<QmlProfilerOverviewModelProxy::QmlOverviewEvent> QmlProfilerOverviewModelProxy::getData() const
+{
+ return d->data;
+}
+void QmlProfilerOverviewModelProxy::clear()
+{
+ d->data.clear();
+ d->rowCount = 0;
+}
+
+void QmlProfilerOverviewModelProxy::dataChanged()
+{
+ loadData();
+
+ emit dataAvailable();
+ emit emptyChanged();
+}
+
+void QmlProfilerOverviewModelProxy::detectBindingLoops()
+{
+ QStack<int> callStack;
+
+ static QVector<int> acceptedTypes =
+ QVector<int>() << QmlDebug::Compiling << QmlDebug::Creating
+ << QmlDebug::Binding << QmlDebug::HandlingSignal;
+
+
+ for (int i = 0; i < d->data.size(); ++i) {
+ QmlOverviewEvent *event = &d->data[i];
+
+ if (!acceptedTypes.contains(event->eventType))
+ continue;
+
+ QmlOverviewEvent *potentialParent = callStack.isEmpty() ? 0 : &d->data[callStack.top()];
+
+ while (potentialParent
+ && !(potentialParent->startTime + potentialParent->duration > event->startTime)) {
+ callStack.pop();
+ potentialParent = callStack.isEmpty() ? 0 : &d->data[callStack.top()];
+ }
+
+ // check whether event is already in stack
+ for (int ii = 0; ii < callStack.size(); ++ii) {
+ if (d->data[callStack.at(ii)].eventId == event->eventId) {
+ event->bindingLoopHead = callStack.at(ii);
+ break;
+ }
+ }
+
+ callStack.push(i);
+ }
+}
+
+bool compareEvents(const QmlProfilerOverviewModelProxy::QmlOverviewEvent &t1, const QmlProfilerOverviewModelProxy::QmlOverviewEvent &t2)
+{
+ return t1.startTime < t2.startTime;
+}
+
+
+void QmlProfilerOverviewModelProxy::loadData()
+{
+ clear();
+ QmlProfilerSimpleModel *simpleModel = d->modelManager->simpleModel();
+ if (simpleModel->isEmpty())
+ return;
+
+ QHash <QString, int> eventIdDict;
+
+ const QVector<QmlProfilerSimpleModel::QmlEventData> eventList = simpleModel->getEvents();
+ foreach (const QmlProfilerSimpleModel::QmlEventData &event, eventList) {
+
+ // find event id
+ QString eventHash = QmlProfilerSimpleModel::getHashString(event);
+ int eventId;
+ if (eventIdDict.contains(eventHash)) {
+ eventId = eventIdDict[eventHash];
+ } else {
+ eventId = eventIdDict.count();
+ eventIdDict.insert(eventHash, eventId);
+ }
+
+ QmlOverviewEvent newEvent = {
+ event.eventType,
+ event.startTime,
+ event.duration,
+ 1.0, // height
+ eventId, // eventId
+ event.eventType, // eventType
+ -1 // bindingLoopHead
+ };
+ d->data.append(newEvent);
+
+ if (event.eventType > d->rowCount)
+ d->rowCount = event.eventType;
+ }
+
+ qSort(d->data.begin(), d->data.end(), compareEvents);
+
+ detectBindingLoops();
+}
+
+/////////////////// QML interface
+
+bool QmlProfilerOverviewModelProxy::empty() const
+{
+ return count() == 0;
+}
+
+int QmlProfilerOverviewModelProxy::count() const
+{
+ return d->data.count();
+}
+
+qint64 QmlProfilerOverviewModelProxy::traceStartTime() const
+{
+ return d->modelManager->traceTime()->startTime();
+}
+
+qint64 QmlProfilerOverviewModelProxy::traceDuration() const
+{
+ return d->modelManager->traceTime()->duration();
+}
+
+qint64 QmlProfilerOverviewModelProxy::traceEndTime() const
+{
+ return d->modelManager->traceTime()->endTime();
+}
+
+qint64 QmlProfilerOverviewModelProxy::getStartTime(int index) const
+{
+ return d->data[index].startTime;
+}
+
+qint64 QmlProfilerOverviewModelProxy::getDuration(int index) const
+{
+ return d->data[index].duration;
+}
+
+int QmlProfilerOverviewModelProxy::getType(int index) const
+{
+ return d->data[index].row;
+}
+
+int QmlProfilerOverviewModelProxy::getEventId(int index) const
+{
+ return d->data[index].eventId;
+}
+
+int QmlProfilerOverviewModelProxy::getBindingLoopDest(int index) const
+{
+ return d->data[index].bindingLoopHead;
+}
+
+}
+}
diff --git a/plugins/qmlprofiler/qmlprofileroverviewmodelproxy.h b/plugins/qmlprofiler/qmlprofileroverviewmodelproxy.h
new file mode 100644
index 0000000000..cc82f53b96
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofileroverviewmodelproxy.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+
+#ifndef QMLPROFILEROVERVIEWMODELPROXY_H
+#define QMLPROFILEROVERVIEWMODELPROXY_H
+
+#include <QObject>
+
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerModelManager;
+
+class QmlProfilerOverviewModelProxy : public QObject
+{
+ Q_PROPERTY(bool empty READ empty NOTIFY emptyChanged)
+
+ Q_OBJECT
+public:
+ struct QmlOverviewEvent {
+ int row;
+ qint64 startTime;
+ qint64 duration;
+ double height;
+ int eventId;
+ int eventType;
+ int bindingLoopHead;
+ };
+
+ QmlProfilerOverviewModelProxy(QmlProfilerModelManager *modelManager, QObject *parent = 0);
+ ~QmlProfilerOverviewModelProxy();
+
+ const QVector<QmlOverviewEvent> getData() const;
+
+ void loadData();
+ Q_INVOKABLE int count() const;
+ Q_INVOKABLE qint64 traceStartTime() const;
+ Q_INVOKABLE qint64 traceDuration() const;
+ Q_INVOKABLE qint64 traceEndTime() const;
+ Q_INVOKABLE qint64 getStartTime(int index) const;
+ Q_INVOKABLE qint64 getDuration(int index) const;
+ Q_INVOKABLE int getType(int index) const;
+ Q_INVOKABLE int getEventId(int index) const;
+ Q_INVOKABLE int getBindingLoopDest(int i) const;
+
+ void clear();
+
+ int rowCount() const;
+
+
+// QML interface
+ bool empty() const;
+
+
+signals:
+ void countChanged();
+ void dataAvailable();
+// void stateChanged();
+ void emptyChanged();
+// void expandedChanged();
+
+private slots:
+ void dataChanged();
+
+private:
+ void detectBindingLoops();
+
+private:
+ class QmlProfilerOverviewModelProxyPrivate;
+ QmlProfilerOverviewModelProxyPrivate *d;
+
+};
+
+}
+}
+
+#endif
diff --git a/plugins/qmlprofiler/qmlprofilerplugin.cpp b/plugins/qmlprofiler/qmlprofilerplugin.cpp
new file mode 100644
index 0000000000..4d49ff1d8a
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerplugin.cpp
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilerplugin.h"
+
+#include "qmlprofilertool.h"
+
+#include <analyzerbase/analyzermanager.h>
+
+#include <QtPlugin>
+
+using namespace Analyzer;
+using namespace QmlProfiler::Internal;
+
+bool QmlProfilerPlugin::debugOutput = false;
+
+bool QmlProfilerPlugin::initialize(const QStringList &arguments, QString *errorString)
+{
+ Q_UNUSED(arguments)
+ Q_UNUSED(errorString)
+
+ StartModes modes;
+ modes.append(StartMode(StartLocal));
+ modes.append(StartMode(StartRemote));
+ AnalyzerManager::addTool(new QmlProfilerTool(this), modes);
+
+ return true;
+}
+
+void QmlProfilerPlugin::extensionsInitialized()
+{
+ // Retrieve objects from the plugin manager's object pool.
+ // "In the extensionsInitialized method, a plugin can be sure that all
+ // plugins that depend on it are completely initialized."
+}
+
+ExtensionSystem::IPlugin::ShutdownFlag QmlProfilerPlugin::aboutToShutdown()
+{
+ // Save settings.
+ // Disconnect from signals that are not needed during shutdown
+ // Hide UI (if you add UI that is not in the main window directly)
+ return SynchronousShutdown;
+}
+
+Q_EXPORT_PLUGIN(QmlProfilerPlugin)
+
diff --git a/plugins/qmlprofiler/qmlprofilerplugin.h b/plugins/qmlprofiler/qmlprofilerplugin.h
new file mode 100644
index 0000000000..aa8d15d730
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerplugin.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERPLUGIN_H
+#define QMLPROFILERPLUGIN_H
+
+#include "qmlprofiler_global.h"
+
+#include <extensionsystem/iplugin.h>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerPlugin : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "QmlProfiler.json")
+
+public:
+ QmlProfilerPlugin() {}
+
+ bool initialize(const QStringList &arguments, QString *errorString);
+ void extensionsInitialized();
+ ShutdownFlag aboutToShutdown();
+
+ static bool debugOutput;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILERPLUGIN_H
+
diff --git a/plugins/qmlprofiler/qmlprofilerprocessedmodel.cpp b/plugins/qmlprofiler/qmlprofilerprocessedmodel.cpp
new file mode 100644
index 0000000000..05902742e8
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerprocessedmodel.cpp
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilerprocessedmodel.h"
+
+#include <qmldebug/qmlprofilereventtypes.h>
+#include <utils/qtcassert.h>
+#include <QUrl>
+#include <QDebug>
+
+namespace QmlProfiler {
+namespace Internal {
+
+QmlDebug::QmlEventLocation getLocation(const QmlProfilerSimpleModel::QmlEventData &event);
+QString getDisplayName(const QmlProfilerSimpleModel::QmlEventData &event);
+QString getInitialDetails(const QmlProfilerSimpleModel::QmlEventData &event);
+
+QmlDebug::QmlEventLocation getLocation(const QmlProfilerSimpleModel::QmlEventData &event)
+{
+ QmlDebug::QmlEventLocation eventLocation = event.location;
+ if (event.eventType == QmlDebug::Compiling && eventLocation.filename.isEmpty()) {
+ eventLocation.filename = getInitialDetails(event);
+ eventLocation.line = 1;
+ eventLocation.column = 1;
+ }
+ return eventLocation;
+}
+
+QString getDisplayName(const QmlProfilerSimpleModel::QmlEventData &event)
+{
+ const QmlDebug::QmlEventLocation eventLocation = getLocation(event);
+ QString displayName;
+
+ // generate hash
+ if (eventLocation.filename.isEmpty()) {
+ displayName = QmlProfilerProcessedModel::tr("<bytecode>");
+ } else {
+ const QString filePath = QUrl(eventLocation.filename).path();
+ displayName = filePath.mid(filePath.lastIndexOf(QLatin1Char('/')) + 1) + QLatin1Char(':') +
+ QString::number(eventLocation.line);
+ }
+
+ return displayName;
+}
+
+QString getInitialDetails(const QmlProfilerSimpleModel::QmlEventData &event)
+{
+ QString details;
+ // generate details string
+ if (event.data.isEmpty())
+ details = QmlProfilerProcessedModel::tr("Source code not available");
+ else {
+ details = event.data.join(QLatin1String(" ")).replace(QLatin1Char('\n'),QLatin1Char(' ')).simplified();
+ QRegExp rewrite(QLatin1String("\\(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);
+ }
+
+ return details;
+}
+
+
+bool compareStartTimes(const QmlProfilerSimpleModel::QmlEventData &t1, const QmlProfilerSimpleModel::QmlEventData &t2)
+{
+ return t1.startTime < t2.startTime;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+QmlProfilerProcessedModel::QmlProfilerProcessedModel(Utils::FileInProjectFinder *fileFinder, QObject *parent)
+ : QmlProfilerSimpleModel(parent)
+ , m_detailsRewriter(new QmlProfilerDetailsRewriter(this, fileFinder))
+ , m_emitChanged(false)
+{
+ connect(m_detailsRewriter, SIGNAL(rewriteDetailsString(int,QString)),
+ this, SLOT(detailsChanged(int,QString)));
+ connect(m_detailsRewriter, SIGNAL(eventDetailsChanged()),
+ this, SLOT(detailsDone()));
+}
+
+QmlProfilerProcessedModel::~QmlProfilerProcessedModel()
+{
+}
+
+void QmlProfilerProcessedModel::clear()
+{
+ m_detailsRewriter->clearRequests();
+ QmlProfilerSimpleModel::clear();
+
+ emit changed();
+ m_emitChanged = false;
+}
+
+void QmlProfilerProcessedModel::complete()
+{
+ // post-processing
+
+ // sort events by start time
+ qSort(eventList.begin(), eventList.end(), compareStartTimes);
+
+ // rewrite strings
+ int n = eventList.count();
+ for (int i = 0; i < n; i++) {
+ QmlEventData *event = &eventList[i];
+ event->location = getLocation(*event);
+ event->displayName = getDisplayName(*event);
+ event->data = QStringList() << getInitialDetails(*event);
+
+ //
+ // request further details from files
+ //
+
+ if (event->eventType != QmlDebug::Binding && event->eventType != QmlDebug::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;
+
+ m_detailsRewriter->requestDetailsForLocation(i, event->location);
+ }
+
+ m_detailsRewriter->reloadDocuments();
+
+ emit changed();
+ m_emitChanged = false;
+}
+
+void QmlProfilerProcessedModel::detailsChanged(int requestId, const QString &newString)
+{
+ QTC_ASSERT(requestId < eventList.count(), return);
+
+ QmlEventData *event = &eventList[requestId];
+ event->data = QStringList(newString);
+
+ m_emitChanged = true;
+}
+
+void QmlProfilerProcessedModel::detailsDone()
+{
+ if (m_emitChanged) {
+ emit changed();
+ m_emitChanged = false;
+ }
+}
+
+}
+}
diff --git a/plugins/qmlprofiler/qmlprofilerprocessedmodel.h b/plugins/qmlprofiler/qmlprofilerprocessedmodel.h
new file mode 100644
index 0000000000..4c293711f2
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerprocessedmodel.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERPROCESSEDMODEL_H
+#define QMLPROFILERPROCESSEDMODEL_H
+
+#include "qmlprofilersimplemodel.h"
+#include "qmlprofilerdetailsrewriter.h"
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerProcessedModel : public QmlProfilerSimpleModel
+{
+ Q_OBJECT
+
+public:
+ explicit QmlProfilerProcessedModel(Utils::FileInProjectFinder *fileFinder, QObject *parent = 0);
+ ~QmlProfilerProcessedModel();
+
+ virtual void clear();
+ virtual void complete();
+
+private slots:
+ void detailsChanged(int requestId, const QString &newString);
+ void detailsDone();
+
+private:
+ QmlProfilerDetailsRewriter *m_detailsRewriter;
+ bool m_emitChanged;
+};
+
+}
+}
+
+#endif
diff --git a/plugins/qmlprofiler/qmlprofilersimplemodel.cpp b/plugins/qmlprofiler/qmlprofilersimplemodel.cpp
new file mode 100644
index 0000000000..c3ce407186
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilersimplemodel.cpp
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilersimplemodel.h"
+#include <QStringList>
+#include <QVector>
+#include <QDebug>
+
+namespace QmlProfiler {
+namespace Internal {
+
+QmlProfilerSimpleModel::QmlProfilerSimpleModel(QObject *parent)
+ : QObject(parent)
+{
+}
+
+QmlProfilerSimpleModel::~QmlProfilerSimpleModel()
+{
+}
+
+void QmlProfilerSimpleModel::clear()
+{
+ eventList.clear();
+ emit changed();
+}
+
+bool QmlProfilerSimpleModel::isEmpty() const
+{
+ return eventList.isEmpty();
+}
+
+const QVector<QmlProfilerSimpleModel::QmlEventData> &QmlProfilerSimpleModel::getEvents() const
+{
+ return eventList;
+}
+
+int QmlProfilerSimpleModel::count() const
+{
+ return eventList.count();
+}
+
+void QmlProfilerSimpleModel::addRangedEvent(int type, int bindingType, qint64 startTime, qint64 duration, const QStringList &data, const QmlDebug::QmlEventLocation &location)
+{
+ QString displayName = QString::fromLatin1("%1:%2").arg(
+ location.filename,
+ QString::number(location.line));
+ QmlEventData eventData = {displayName, type, bindingType, startTime, duration, data, location};
+ eventList.append(eventData);
+}
+
+qint64 QmlProfilerSimpleModel::lastTimeMark() const
+{
+ if (eventList.isEmpty())
+ return 0;
+
+ return eventList.last().startTime + eventList.last().duration;
+}
+
+void QmlProfilerSimpleModel::complete()
+{
+ emit changed();
+}
+
+QString QmlProfilerSimpleModel::getHashString(const QmlProfilerSimpleModel::QmlEventData &event)
+{
+ return QString::fromLatin1("%1:%2:%3:%4").arg(
+ event.location.filename,
+ QString::number(event.location.line),
+ QString::number(event.location.column),
+ QString::number(event.eventType));
+}
+
+
+}
+}
diff --git a/plugins/qmlprofiler/qmlprofilersimplemodel.h b/plugins/qmlprofiler/qmlprofilersimplemodel.h
new file mode 100644
index 0000000000..4b5f674131
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilersimplemodel.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERSIMPLEMODEL_H
+#define QMLPROFILERSIMPLEMODEL_H
+
+#include <QObject>
+#include <QVector>
+#include <QStringList>
+#include "qmldebug/qmlprofilereventlocation.h"
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerModelManager;
+
+// stores the data from the client as-is
+class QmlProfilerSimpleModel : public QObject
+{
+ Q_OBJECT
+public:
+ struct QmlEventData {
+ QString displayName;
+ int eventType;
+ int bindingType;
+ qint64 startTime;
+ qint64 duration;
+ QStringList data;
+ QmlDebug::QmlEventLocation location;
+ };
+
+ explicit QmlProfilerSimpleModel(QObject *parent = 0);
+ ~QmlProfilerSimpleModel();
+
+ virtual void clear();
+ bool isEmpty() const;
+ const QVector<QmlEventData> &getEvents() const;
+ int count() const;
+ void addRangedEvent(int type, int bindingType, qint64 startTime, qint64 length, const QStringList &data, const QmlDebug::QmlEventLocation &location);
+ qint64 lastTimeMark() const;
+ virtual void complete();
+
+ static QString getHashString(const QmlProfilerSimpleModel::QmlEventData &event);
+
+signals:
+ void changed();
+
+protected:
+ QVector<QmlEventData> eventList;
+};
+
+}
+}
+
+#endif
diff --git a/plugins/qmlprofiler/qmlprofilerstatemanager.cpp b/plugins/qmlprofiler/qmlprofilerstatemanager.cpp
new file mode 100644
index 0000000000..953ed9852f
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerstatemanager.cpp
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilerstatemanager.h"
+
+#include <QDebug>
+#include <utils/qtcassert.h>
+
+// uncomment for printing the state changes to debug output
+//#define _DEBUG_PROFILERSTATE_
+
+namespace QmlProfiler {
+namespace Internal {
+
+inline QString stringForState(int state) {
+ switch (state) {
+ case QmlProfilerStateManager::Idle: return QLatin1String("Idle");
+ case QmlProfilerStateManager::AppStarting: return QLatin1String("AppStarting");
+ case QmlProfilerStateManager::AppRunning: return QLatin1String("AppRunning");
+ case QmlProfilerStateManager::AppStopRequested: return QLatin1String("AppStopRequested");
+ case QmlProfilerStateManager::AppReadyToStop: return QLatin1String("AppReadyToStop");
+ case QmlProfilerStateManager::AppStopped: return QLatin1String("AppStopped");
+ case QmlProfilerStateManager::AppDying: return QLatin1String("AppDying");
+ case QmlProfilerStateManager::AppKilled: return QLatin1String("AppKilled");
+ default: break;
+ }
+ return QString();
+}
+
+class QmlProfilerStateManager::QmlProfilerStateManagerPrivate
+{
+public:
+ QmlProfilerStateManagerPrivate(QmlProfilerStateManager *qq) : q(qq) {}
+ ~QmlProfilerStateManagerPrivate() {}
+
+ QmlProfilerStateManager *q;
+
+ QmlProfilerStateManager::QmlProfilerState m_currentState;
+ bool m_clientRecording;
+ bool m_serverRecording;
+};
+QmlProfilerStateManager::QmlProfilerStateManager(QObject *parent) :
+ QObject(parent),d(new QmlProfilerStateManagerPrivate(this))
+{
+ d->m_currentState = Idle;
+ d->m_clientRecording = true;
+ d->m_serverRecording = false;
+}
+
+QmlProfilerStateManager::~QmlProfilerStateManager()
+{
+ delete d;
+}
+
+QmlProfilerStateManager::QmlProfilerState QmlProfilerStateManager::currentState()
+{
+ return d->m_currentState;
+}
+
+bool QmlProfilerStateManager::clientRecording()
+{
+ return d->m_clientRecording;
+}
+
+bool QmlProfilerStateManager::serverRecording()
+{
+ return d->m_serverRecording;
+}
+
+QString QmlProfilerStateManager::currentStateAsString()
+{
+ return stringForState(d->m_currentState);
+}
+
+void QmlProfilerStateManager::setCurrentState(QmlProfilerState newState)
+{
+#ifdef _DEBUG_PROFILERSTATE_
+ qDebug() << "Profiler state change request from" << stringForState(d->m_currentState) << "to" << stringForState(newState);
+#endif
+ QTC_ASSERT(d->m_currentState != newState, /**/);
+ switch (newState) {
+ case Idle:
+ QTC_ASSERT(d->m_currentState == AppStarting ||
+ d->m_currentState == AppStopped ||
+ d->m_currentState == AppKilled,
+ qDebug() << "from" << stringForState(d->m_currentState));
+ break;
+ case AppStarting:
+ QTC_ASSERT(d->m_currentState == Idle,
+ qDebug() << "from" << stringForState(d->m_currentState));
+ break;
+ case AppRunning:
+ QTC_ASSERT(d->m_currentState == AppStarting,
+ qDebug() << "from" << stringForState(d->m_currentState));
+ break;
+ case AppStopRequested:
+ QTC_ASSERT(d->m_currentState == AppRunning,
+ qDebug() << "from" << stringForState(d->m_currentState));
+ break;
+ case AppReadyToStop:
+ QTC_ASSERT(d->m_currentState == AppStopRequested,
+ qDebug() << "from" << stringForState(d->m_currentState));
+ break;
+ case AppStopped:
+ QTC_ASSERT(d->m_currentState == AppReadyToStop ||
+ d->m_currentState == AppDying,
+ qDebug() << "from" << stringForState(d->m_currentState));
+ break;
+ case AppDying:
+ QTC_ASSERT(d->m_currentState == AppRunning,
+ qDebug() << "from" << stringForState(d->m_currentState));
+ break;
+ case AppKilled:
+ QTC_ASSERT(d->m_currentState == AppDying,
+ qDebug() << "from" << stringForState(d->m_currentState));
+ break;
+ default: {
+ const QString message = QString::fromLatin1("Switching to unknown state in %1:%2").arg(QString::fromLatin1(__FILE__), QString::number(__LINE__));
+ qWarning("%s", qPrintable(message));
+ }
+ break;
+ }
+
+ d->m_currentState = newState;
+ emit stateChanged();
+}
+
+void QmlProfilerStateManager::setClientRecording(bool recording)
+{
+#ifdef _DEBUG_PROFILERSTATE_
+ qDebug() << "Setting client recording flag from" << d->m_serverRecording << "to" << recording;
+#endif
+ if (d->m_clientRecording != recording) {
+ d->m_clientRecording = recording;
+ emit clientRecordingChanged();
+ }
+}
+
+void QmlProfilerStateManager::setServerRecording(bool recording)
+{
+#ifdef _DEBUG_PROFILERSTATE_
+ qDebug() << "Setting server recording flag from" << d->m_serverRecording << "to" << recording;
+#endif
+ if (d->m_serverRecording != recording) {
+ d->m_serverRecording = recording;
+ emit serverRecordingChanged();
+ }
+}
+
+}
+}
diff --git a/plugins/qmlprofiler/qmlprofilerstatemanager.h b/plugins/qmlprofiler/qmlprofilerstatemanager.h
new file mode 100644
index 0000000000..be93fd4d32
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerstatemanager.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERSTATEMANAGER_H
+#define QMLPROFILERSTATEMANAGER_H
+
+#include <QObject>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerStateManager : public QObject
+{
+ Q_OBJECT
+public:
+ enum QmlProfilerState {
+ Idle,
+ AppStarting,
+ AppRunning,
+ AppStopRequested,
+ AppReadyToStop,
+ AppStopped,
+ AppDying,
+ AppKilled
+ };
+
+ explicit QmlProfilerStateManager(QObject *parent = 0);
+ ~QmlProfilerStateManager();
+
+ QmlProfilerState currentState();
+ bool clientRecording();
+ bool serverRecording();
+
+ QString currentStateAsString();
+
+signals:
+ void stateChanged();
+ void clientRecordingChanged();
+ void serverRecordingChanged();
+
+public slots:
+ void setCurrentState(QmlProfilerState newState);
+ void setClientRecording(bool recording);
+ void setServerRecording(bool recording);
+
+private:
+ class QmlProfilerStateManagerPrivate;
+ QmlProfilerStateManagerPrivate *d;
+};
+
+}
+}
+
+#endif // QMLPROFILERSTATEMANAGER_H
diff --git a/plugins/qmlprofiler/qmlprofilerstatewidget.cpp b/plugins/qmlprofiler/qmlprofilerstatewidget.cpp
new file mode 100644
index 0000000000..7887dec61e
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerstatewidget.cpp
@@ -0,0 +1,282 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilerstatewidget.h"
+
+#include <QPainter>
+
+#include <QVBoxLayout>
+#include <QLabel>
+#include <QProgressBar>
+#include <QTime>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerStateWidget::QmlProfilerStateWidgetPrivate
+{
+ public:
+ QmlProfilerStateWidgetPrivate(QmlProfilerStateWidget *qq) { Q_UNUSED(qq); }
+
+ QLabel *text;
+ QProgressBar *progressBar;
+ QPixmap shadowPic;
+
+ QmlProfilerStateManager *m_profilerState;
+ QmlProfilerModelManager *m_modelManager;
+
+ bool isRecording;
+ bool appKilled;
+ bool emptyList;
+ bool traceAvailable;
+ bool loadingDone;
+ QTime profilingTimer;
+ qint64 estimatedProfilingTime;
+};
+
+QmlProfilerStateWidget::QmlProfilerStateWidget(QmlProfilerStateManager *stateManager,
+ QmlProfilerModelManager *modelManager, QWidget *parent)
+ : QWidget(parent), d(new QmlProfilerStateWidgetPrivate(this))
+{
+ setObjectName(QLatin1String("QML Profiler State Display"));
+
+ // UI elements
+ QVBoxLayout *layout = new QVBoxLayout(this);
+ resize(200,70);
+
+ d->shadowPic.load(QLatin1String(":/qmlprofiler/dialog_shadow.png"));
+
+ d->text = new QLabel(this);
+ d->text->setAlignment(Qt::AlignCenter);
+ layout->addWidget(d->text);
+
+ d->progressBar = new QProgressBar(this);
+ layout->addWidget(d->progressBar);
+ d->progressBar->setVisible(false);
+
+ setLayout(layout);
+
+ // internal state
+ d->isRecording = false;
+ d->appKilled = false;
+ d->traceAvailable = false;
+ d->loadingDone = true;
+ d->emptyList = true;
+
+ // profiler state
+ d->m_modelManager = modelManager;
+ connect(d->m_modelManager,SIGNAL(stateChanged()), this, SLOT(dataStateChanged()));
+ connect(d->m_modelManager,SIGNAL(countChanged()), this, SLOT(dataStateChanged()));
+ d->m_profilerState = stateManager;
+ connect(d->m_profilerState,SIGNAL(stateChanged()), this, SLOT(profilerStateChanged()));
+ connect(d->m_profilerState, SIGNAL(serverRecordingChanged()),
+ this, SLOT(profilerStateChanged()));
+
+ updateDisplay();
+ connect(parent,SIGNAL(resized()),this,SLOT(reposition()));
+}
+
+QmlProfilerStateWidget::~QmlProfilerStateWidget()
+{
+ delete d;
+}
+
+void QmlProfilerStateWidget::reposition()
+{
+ QWidget *parentWidget = qobject_cast<QWidget *>(parent());
+ // positioning it at 2/3 height (it looks better)
+ move(parentWidget->width()/2 - width()/2, parentWidget->height()/3 - height()/2);
+}
+
+void QmlProfilerStateWidget::paintEvent(QPaintEvent *event)
+{
+ QWidget::paintEvent(event);
+
+ QPainter painter(this);
+ painter.save();
+
+ // Shadow
+ // there is no actual qpainter borderimage, hacking it here
+ int borderWidth = 4;
+
+ // topleft
+ painter.drawPixmap(QRect(0, 0, borderWidth, borderWidth),
+ d->shadowPic,
+ QRect(0, 0, borderWidth, borderWidth));
+ // topright
+ painter.drawPixmap(QRect(width()-borderWidth, 0, borderWidth, borderWidth),
+ d->shadowPic,
+ QRect(d->shadowPic.width()-borderWidth, 0, borderWidth, borderWidth));
+ // bottomleft
+ painter.drawPixmap(QRect(0, height()-borderWidth, borderWidth, borderWidth),
+ d->shadowPic,
+ QRect(0, d->shadowPic.height()-borderWidth, borderWidth, borderWidth));
+ // bottomright
+ painter.drawPixmap(QRect(width()-borderWidth, height()-borderWidth, borderWidth, borderWidth),
+ d->shadowPic,
+ QRect(d->shadowPic.width()-borderWidth,
+ d->shadowPic.height()-borderWidth,
+ borderWidth,
+ borderWidth));
+ // top
+ painter.drawPixmap(QRect(borderWidth, 0, width()-2*borderWidth, borderWidth),
+ d->shadowPic,
+ QRect(borderWidth, 0, d->shadowPic.width()-2*borderWidth, borderWidth));
+ // bottom
+ painter.drawPixmap(QRect(borderWidth, height()-borderWidth, width()-2*borderWidth, borderWidth),
+ d->shadowPic,
+ QRect(borderWidth,
+ d->shadowPic.height()-borderWidth,
+ d->shadowPic.width()-2*borderWidth,
+ borderWidth));
+ // left
+ painter.drawPixmap(QRect(0, borderWidth, borderWidth, height()-2*borderWidth),
+ d->shadowPic,
+ QRect(0, borderWidth, borderWidth, d->shadowPic.height()-2*borderWidth));
+ // right
+ painter.drawPixmap(QRect(width()-borderWidth, borderWidth, borderWidth, height()-2*borderWidth),
+ d->shadowPic,
+ QRect(d->shadowPic.width()-borderWidth,
+ borderWidth,
+ borderWidth,
+ d->shadowPic.height()-2*borderWidth));
+ // center
+ painter.drawPixmap(QRect(borderWidth, borderWidth, width()-2*borderWidth, height()-2*borderWidth),
+ d->shadowPic,
+ QRect(borderWidth,
+ borderWidth,
+ d->shadowPic.width()-2*borderWidth,
+ d->shadowPic.height()-2*borderWidth));
+
+
+ // Background
+ painter.setBrush(QColor("#E0E0E0"));
+ painter.setPen(QColor("#666666"));
+ painter.drawRoundedRect(QRect(borderWidth, 0, width()-2*borderWidth, height()-borderWidth), 6, 6);
+
+ // restore painter
+ painter.restore();
+
+}
+
+void QmlProfilerStateWidget::updateDisplay()
+{
+ // When datamodel is acquiring data
+ if (!d->loadingDone && !d->emptyList && !d->appKilled) {
+ setVisible(true);
+ d->text->setText(tr("Loading data"));
+ if (d->isRecording) {
+ d->isRecording = false;
+ d->estimatedProfilingTime = d->profilingTimer.elapsed();
+ }
+ d->progressBar->setMaximum(d->estimatedProfilingTime);
+ d->progressBar->setValue(d->m_modelManager->traceTime()->endTime() * 1e-6);
+ d->progressBar->setVisible(true);
+ resize(300,70);
+ reposition();
+ return;
+ }
+
+ // When application is being profiled
+ if (d->isRecording) {
+ setVisible(true);
+ d->progressBar->setVisible(false);
+ d->text->setText(tr("Profiling application"));
+ resize(200,70);
+ reposition();
+ return;
+ }
+
+ // After profiling, there is an empty trace
+ if (d->traceAvailable && d->loadingDone && d->emptyList) {
+ setVisible(true);
+ d->progressBar->setVisible(false);
+ d->text->setText(tr("No QML events recorded"));
+ resize(200,70);
+ reposition();
+ return;
+ }
+
+ // Application died before all data could be read
+ if (!d->loadingDone && !d->emptyList && d->appKilled) {
+ setVisible(true);
+ d->text->setText(tr("Application stopped before loading all data"));
+ if (d->isRecording) {
+ d->isRecording = false;
+ d->estimatedProfilingTime = d->profilingTimer.elapsed();
+ }
+ d->progressBar->setMaximum(d->estimatedProfilingTime);
+ d->progressBar->setValue(d->m_modelManager->traceTime()->endTime() * 1e-6);
+ d->progressBar->setVisible(true);
+ resize(300,70);
+ reposition();
+ return;
+ }
+
+ // Everything empty (base state), commented out now but needed in the future.
+// if (d->emptyList && d->loadingDone) {
+// setVisible(true);
+// d->progressBar->setVisible(false);
+// d->text->setText(tr("Profiler ready"));
+// resize(200,70);
+// parentResized();
+// return;
+// }
+
+ // There is a trace on view, hide this dialog
+ setVisible(false);
+}
+
+void QmlProfilerStateWidget::dataStateChanged()
+{
+ d->loadingDone = d->m_modelManager->state() == QmlProfilerDataState::Done ||
+ d->m_modelManager->state() == QmlProfilerDataState::Empty;
+ d->traceAvailable = d->m_modelManager->traceTime()->duration() > 0;
+ d->emptyList = d->m_modelManager->isEmpty();
+ updateDisplay();
+}
+
+void QmlProfilerStateWidget::profilerStateChanged()
+{
+ if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppKilled)
+ d->appKilled = true;
+ else
+ if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppStarting)
+ d->appKilled = false;
+
+ d->isRecording = d->m_profilerState->serverRecording();
+ if (d->isRecording)
+ d->profilingTimer.start();
+ else
+ d->estimatedProfilingTime = d->profilingTimer.elapsed();
+ updateDisplay();
+}
+
+}
+}
diff --git a/plugins/qmlprofiler/qmlprofilerstatewidget.h b/plugins/qmlprofiler/qmlprofilerstatewidget.h
new file mode 100644
index 0000000000..85ff96798a
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerstatewidget.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERSTATEWIDGET_H
+#define QMLPROFILERSTATEWIDGET_H
+
+#include <QWidget>
+
+#include "qmlprofilerstatemanager.h"
+#include "qmlprofilermodelmanager.h"
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerStateWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit QmlProfilerStateWidget(QmlProfilerStateManager *stateManager,
+ QmlProfilerModelManager *modelManager, QWidget *parent = 0);
+ ~QmlProfilerStateWidget();
+
+private slots:
+ void updateDisplay();
+ void dataStateChanged();
+ void profilerStateChanged();
+ void reposition();
+
+protected:
+ void paintEvent(QPaintEvent *event);
+
+private:
+ class QmlProfilerStateWidgetPrivate;
+ QmlProfilerStateWidgetPrivate *d;
+};
+
+}
+}
+
+#endif // QMLPROFILERSTATEWIDGET_H
diff --git a/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.cpp b/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.cpp
new file mode 100644
index 0000000000..cea576723a
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.cpp
@@ -0,0 +1,624 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilertimelinemodelproxy.h"
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilersimplemodel.h"
+
+#include <QVector>
+#include <QHash>
+#include <QUrl>
+#include <QString>
+#include <QStack>
+
+#include <QDebug>
+
+namespace QmlProfiler {
+namespace Internal {
+
+struct CategorySpan {
+ bool expanded;
+ int expandedRows;
+ int contractedRows;
+};
+
+class QmlProfilerTimelineModelProxy::QmlProfilerTimelineModelProxyPrivate
+{
+public:
+ QmlProfilerTimelineModelProxyPrivate(QmlProfilerTimelineModelProxy *qq) : q(qq) {}
+ ~QmlProfilerTimelineModelProxyPrivate() {}
+
+ // convenience functions
+ void prepare();
+ void computeNestingContracted();
+ void computeExpandedLevels();
+ void computeBaseEventIndexes();
+ void buildEndTimeList();
+ void findBindingLoops();
+
+ QString displayTime(double time);
+
+ QVector <QmlProfilerTimelineModelProxy::QmlRangeEventData> eventDict;
+ QVector <QString> eventHashes;
+ QVector <QmlProfilerTimelineModelProxy::QmlRangeEventStartInstance> startTimeData;
+ QVector <QmlProfilerTimelineModelProxy::QmlRangeEventEndInstance> endTimeData;
+ QVector <CategorySpan> categorySpan;
+
+ QmlProfilerModelManager *modelManager;
+ QmlProfilerTimelineModelProxy *q;
+};
+
+QmlProfilerTimelineModelProxy::QmlProfilerTimelineModelProxy(QmlProfilerModelManager *modelManager, QObject *parent)
+ : QObject(parent), d(new QmlProfilerTimelineModelProxyPrivate(this))
+{
+ d->modelManager = modelManager;
+ connect(d->modelManager->simpleModel(),SIGNAL(changed()),this,SLOT(dataChanged()));
+}
+
+QmlProfilerTimelineModelProxy::~QmlProfilerTimelineModelProxy()
+{
+ delete d;
+}
+
+const QVector<QmlProfilerTimelineModelProxy::QmlRangeEventStartInstance> QmlProfilerTimelineModelProxy::getData() const
+{
+ return d->startTimeData;
+}
+
+const QVector<QmlProfilerTimelineModelProxy::QmlRangeEventStartInstance> QmlProfilerTimelineModelProxy::getData(qint64 fromTime, qint64 toTime) const
+{
+ int fromIndex = findFirstIndex(fromTime);
+ int toIndex = findLastIndex(toTime);
+ if (fromIndex != -1 && toIndex > fromIndex)
+ return d->startTimeData.mid(fromIndex, toIndex - fromIndex + 1);
+ else
+ return QVector<QmlProfilerTimelineModelProxy::QmlRangeEventStartInstance>();
+}
+
+void QmlProfilerTimelineModelProxy::clear()
+{
+ d->eventDict.clear();
+ d->eventHashes.clear();
+ d->startTimeData.clear();
+ d->endTimeData.clear();
+ d->categorySpan.clear();
+}
+
+void QmlProfilerTimelineModelProxy::dataChanged()
+{
+ loadData();
+
+ emit stateChanged();
+ emit dataAvailable();
+ emit emptyChanged();
+}
+
+void QmlProfilerTimelineModelProxy::QmlProfilerTimelineModelProxyPrivate::prepare()
+{
+ categorySpan.clear();
+ for (int i = 0; i < QmlDebug::MaximumQmlEventType; i++) {
+ CategorySpan newCategory = {false, 1, 1};
+ categorySpan << newCategory;
+ }
+}
+
+bool compareStartTimes(const QmlProfilerTimelineModelProxy::QmlRangeEventStartInstance&t1, const QmlProfilerTimelineModelProxy::QmlRangeEventStartInstance &t2)
+{
+ return t1.startTime < t2.startTime;
+}
+
+bool compareEndTimes(const QmlProfilerTimelineModelProxy::QmlRangeEventEndInstance &t1, const QmlProfilerTimelineModelProxy::QmlRangeEventEndInstance &t2)
+{
+ return t1.endTime < t2.endTime;
+}
+
+void QmlProfilerTimelineModelProxy::loadData()
+{
+ clear();
+ QmlProfilerSimpleModel *simpleModel = d->modelManager->simpleModel();
+ if (simpleModel->isEmpty())
+ return;
+
+ int lastEventId = 0;
+
+ d->prepare();
+
+ // collect events
+ const QVector<QmlProfilerSimpleModel::QmlEventData> eventList = simpleModel->getEvents();
+ foreach (const QmlProfilerSimpleModel::QmlEventData &event, eventList) {
+ QString eventHash = QmlProfilerSimpleModel::getHashString(event);
+
+ // store in dictionary
+ if (!d->eventHashes.contains(eventHash)) {
+ QmlRangeEventData rangeEventData = {
+ event.displayName,
+ event.data.join(QLatin1String(" ")),
+ event.location,
+ (QmlDebug::QmlEventType)event.eventType,
+// event.bindingType,
+// 1,
+ lastEventId++ // event id
+ };
+ d->eventDict << rangeEventData;
+ d->eventHashes << eventHash;
+ }
+
+ // store starttime-based instance
+ QmlRangeEventStartInstance eventStartInstance = {
+ event.startTime,
+ event.duration,
+ QmlDebug::Constants::QML_MIN_LEVEL, // displayRowExpanded;
+ QmlDebug::Constants::QML_MIN_LEVEL, // displayRowCollapsed;
+ 1,
+ d->eventHashes.indexOf(eventHash), // event id
+ -1 // bindingLoopHead
+ };
+ d->startTimeData.append(eventStartInstance);
+ }
+
+ qSort(d->startTimeData.begin(), d->startTimeData.end(), compareStartTimes);
+
+ // compute nestingLevel - nonexpanded
+ d->computeNestingContracted();
+
+ // compute nestingLevel - expanded
+ d->computeExpandedLevels();
+
+ d->computeBaseEventIndexes();
+
+ // populate endtimelist
+ d->buildEndTimeList();
+
+ d->findBindingLoops();
+
+ emit countChanged();
+}
+
+void QmlProfilerTimelineModelProxy::QmlProfilerTimelineModelProxyPrivate::computeNestingContracted()
+{
+ int i;
+ int eventCount = startTimeData.count();
+
+ QHash<int, qint64> endtimesPerLevel;
+ QList<int> nestingLevels;
+ QList< QHash<int, qint64> > endtimesPerNestingLevel;
+ int level = QmlDebug::Constants::QML_MIN_LEVEL;
+ endtimesPerLevel[QmlDebug::Constants::QML_MIN_LEVEL] = 0;
+ int lastBaseEventIndex = 0;
+ qint64 lastBaseEventEndTime = modelManager->traceTime()->startTime();
+
+ for (i = 0; i < QmlDebug::MaximumQmlEventType; i++) {
+ nestingLevels << QmlDebug::Constants::QML_MIN_LEVEL;
+ QHash<int, qint64> dummyHash;
+ dummyHash[QmlDebug::Constants::QML_MIN_LEVEL] = 0;
+ endtimesPerNestingLevel << dummyHash;
+ }
+
+ for (i = 0; i < eventCount; i++) {
+ qint64 st = startTimeData[i].startTime;
+ int type = q->getEventType(i);
+
+ // general level
+ if (endtimesPerLevel[level] > st) {
+ level++;
+ } else {
+ while (level > QmlDebug::Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= st)
+ level--;
+ }
+ endtimesPerLevel[level] = st + startTimeData[i].duration;
+
+ // per type
+ if (endtimesPerNestingLevel[type][nestingLevels[type]] > st) {
+ nestingLevels[type]++;
+ } else {
+ while (nestingLevels[type] > QmlDebug::Constants::QML_MIN_LEVEL &&
+ endtimesPerNestingLevel[type][nestingLevels[type]-1] <= st)
+ nestingLevels[type]--;
+ }
+ endtimesPerNestingLevel[type][nestingLevels[type]] =
+ st + startTimeData[i].duration;
+
+ startTimeData[i].displayRowCollapsed = nestingLevels[type];
+
+ // todo: this should go to another method
+ if (level == QmlDebug::Constants::QML_MIN_LEVEL) {
+ if (lastBaseEventEndTime < startTimeData[i].startTime) {
+ lastBaseEventIndex = i;
+ lastBaseEventEndTime = startTimeData[i].startTime + startTimeData[i].duration;
+ }
+ }
+ startTimeData[i].baseEventIndex = lastBaseEventIndex;
+ }
+
+ // nestingdepth
+ for (i = 0; i < eventCount; i++) {
+ int eventType = q->getEventType(i);
+ if (categorySpan[eventType].contractedRows <= startTimeData[i].displayRowCollapsed)
+ categorySpan[eventType].contractedRows = startTimeData[i].displayRowCollapsed + 1;
+ }
+}
+
+void QmlProfilerTimelineModelProxy::QmlProfilerTimelineModelProxyPrivate::computeExpandedLevels()
+{
+ QHash<int, int> eventRow;
+ int eventCount = startTimeData.count();
+ for (int i = 0; i < eventCount; i++) {
+ int eventId = startTimeData[i].eventId;
+ int eventType = eventDict[eventId].eventType;
+ if (!eventRow.contains(eventId)) {
+ eventRow[eventId] = categorySpan[eventType].expandedRows++;
+ }
+ startTimeData[i].displayRowExpanded = eventRow[eventId];
+ }
+}
+
+void QmlProfilerTimelineModelProxy::QmlProfilerTimelineModelProxyPrivate::computeBaseEventIndexes()
+{
+ // TODO
+}
+
+void QmlProfilerTimelineModelProxy::QmlProfilerTimelineModelProxyPrivate::buildEndTimeList()
+{
+ endTimeData.clear();
+
+ int eventCount = startTimeData.count();
+ for (int i = 0; i < eventCount; i++) {
+ QmlProfilerTimelineModelProxy::QmlRangeEventEndInstance endInstance = {
+ i,
+ startTimeData[i].startTime + startTimeData[i].duration
+ };
+
+ endTimeData << endInstance;
+ }
+
+ qSort(endTimeData.begin(), endTimeData.end(), compareEndTimes);
+}
+
+void QmlProfilerTimelineModelProxy::QmlProfilerTimelineModelProxyPrivate::findBindingLoops()
+{
+ typedef QPair<QString, int> CallStackEntry;
+ QStack<CallStackEntry> callStack;
+
+ for (int i = 0; i < startTimeData.size(); ++i) {
+ QmlRangeEventStartInstance *event = &startTimeData[i];
+
+ QmlProfilerTimelineModelProxy::QmlRangeEventData data = eventDict.at(event->eventId);
+
+ static QVector<QmlDebug::QmlEventType> acceptedTypes =
+ QVector<QmlDebug::QmlEventType>() << QmlDebug::Compiling << QmlDebug::Creating
+ << QmlDebug::Binding << QmlDebug::HandlingSignal;
+
+ if (!acceptedTypes.contains(data.eventType))
+ continue;
+
+ const QString eventHash = eventHashes.at(event->eventId);
+ const QmlRangeEventStartInstance *potentialParent = callStack.isEmpty()
+ ? 0 : &startTimeData[callStack.top().second];
+
+ while (potentialParent
+ && !(potentialParent->startTime + potentialParent->duration > event->startTime)) {
+ callStack.pop();
+ potentialParent = callStack.isEmpty() ? 0
+ : &startTimeData[callStack.top().second];
+ }
+
+ // check whether event is already in stack
+ for (int ii = 0; ii < callStack.size(); ++ii) {
+ if (callStack.at(ii).first == eventHash) {
+ event->bindingLoopHead = callStack.at(ii).second;
+ break;
+ }
+ }
+
+
+ CallStackEntry newEntry(eventHash, i);
+ callStack.push(newEntry);
+ }
+
+}
+
+/////////////////// QML interface
+
+bool QmlProfilerTimelineModelProxy::empty() const
+{
+ return count() == 0;
+}
+
+int QmlProfilerTimelineModelProxy::count() const
+{
+ return d->startTimeData.count();
+}
+
+qint64 QmlProfilerTimelineModelProxy::lastTimeMark() const
+{
+ return d->startTimeData.last().startTime + d->startTimeData.last().duration;
+}
+
+qint64 QmlProfilerTimelineModelProxy::traceStartTime() const
+{
+ return d->modelManager->traceTime()->startTime();
+}
+
+qint64 QmlProfilerTimelineModelProxy::traceEndTime() const
+{
+ return d->modelManager->traceTime()->endTime();
+}
+
+qint64 QmlProfilerTimelineModelProxy::traceDuration() const
+{
+ return d->modelManager->traceTime()->duration();
+}
+
+int QmlProfilerTimelineModelProxy::getState() const
+{
+ // TODO: connect statechanged
+ return (int)d->modelManager->state();
+}
+
+void QmlProfilerTimelineModelProxy::setExpanded(int category, bool expanded)
+{
+ d->categorySpan[category].expanded = expanded;
+ emit expandedChanged();
+}
+
+int QmlProfilerTimelineModelProxy::categoryDepth(int categoryIndex) const
+{
+ if (d->categorySpan.count() <= categoryIndex)
+ return 1;
+ if (d->categorySpan[categoryIndex].expanded)
+ return d->categorySpan[categoryIndex].expandedRows;
+ else
+ return d->categorySpan[categoryIndex].contractedRows;
+}
+
+int QmlProfilerTimelineModelProxy::categoryCount() const
+{
+ return 5;
+}
+
+const QString QmlProfilerTimelineModelProxy::categoryLabel(int categoryIndex) const
+{
+ switch (categoryIndex) {
+ case 0: return tr("Painting"); break;
+ case 1: return tr("Compiling"); break;
+ case 2: return tr("Creating"); break;
+ case 3: return tr("Binding"); break;
+ case 4: return tr("Handling Signal"); break;
+ default: return QString();
+ }
+}
+
+
+int QmlProfilerTimelineModelProxy::findFirstIndex(qint64 startTime) const
+{
+ int candidate = -1;
+ // in the "endtime" list, find the first event that ends after startTime
+ if (d->endTimeData.isEmpty())
+ return 0; // -1
+ if (d->endTimeData.count() == 1 || d->endTimeData.first().endTime >= startTime)
+ candidate = 0;
+ else
+ if (d->endTimeData.last().endTime <= startTime)
+ return 0; // -1
+
+ if (candidate == -1)
+ {
+ int fromIndex = 0;
+ int toIndex = d->endTimeData.count()-1;
+ while (toIndex - fromIndex > 1) {
+ int midIndex = (fromIndex + toIndex)/2;
+ if (d->endTimeData[midIndex].endTime < startTime)
+ fromIndex = midIndex;
+ else
+ toIndex = midIndex;
+ }
+
+ candidate = toIndex;
+ }
+
+ int eventIndex = d->endTimeData[candidate].startTimeIndex;
+ return d->startTimeData[eventIndex].baseEventIndex;
+
+}
+
+int QmlProfilerTimelineModelProxy::findFirstIndexNoParents(qint64 startTime) const
+{
+ int candidate = -1;
+ // in the "endtime" list, find the first event that ends after startTime
+ if (d->endTimeData.isEmpty())
+ return 0; // -1
+ if (d->endTimeData.count() == 1 || d->endTimeData.first().endTime >= startTime)
+ candidate = 0;
+ else
+ if (d->endTimeData.last().endTime <= startTime)
+ return 0; // -1
+
+ if (candidate == -1) {
+ int fromIndex = 0;
+ int toIndex = d->endTimeData.count()-1;
+ while (toIndex - fromIndex > 1) {
+ int midIndex = (fromIndex + toIndex)/2;
+ if (d->endTimeData[midIndex].endTime < startTime)
+ fromIndex = midIndex;
+ else
+ toIndex = midIndex;
+ }
+
+ candidate = toIndex;
+ }
+
+ int ndx = d->endTimeData[candidate].startTimeIndex;
+
+ return ndx;
+}
+
+int QmlProfilerTimelineModelProxy::findLastIndex(qint64 endTime) const
+{
+ // in the "starttime" list, find the last event that starts before endtime
+ if (d->startTimeData.isEmpty())
+ return 0; // -1
+ if (d->startTimeData.first().startTime >= endTime)
+ return 0; // -1
+ if (d->startTimeData.count() == 1)
+ return 0;
+ if (d->startTimeData.last().startTime <= endTime)
+ return d->startTimeData.count()-1;
+
+ int fromIndex = 0;
+ int toIndex = d->startTimeData.count()-1;
+ while (toIndex - fromIndex > 1) {
+ int midIndex = (fromIndex + toIndex)/2;
+ if (d->startTimeData[midIndex].startTime < endTime)
+ fromIndex = midIndex;
+ else
+ toIndex = midIndex;
+ }
+
+ return fromIndex;
+}
+
+int QmlProfilerTimelineModelProxy::getEventType(int index) const
+{
+
+ return getRangeEventData(index).eventType;
+}
+
+const QmlProfilerTimelineModelProxy::QmlRangeEventData &QmlProfilerTimelineModelProxy::getRangeEventData(int index) const
+{
+ // TODO: remove -> use accessors
+ return d->eventDict[d->startTimeData[index].eventId];
+}
+
+int QmlProfilerTimelineModelProxy::getEventRow(int index) const
+{
+ if (d->categorySpan[getEventType(index)].expanded)
+ return d->startTimeData[index].displayRowExpanded;
+ else
+ return d->startTimeData[index].displayRowCollapsed;
+}
+
+qint64 QmlProfilerTimelineModelProxy::getDuration(int index) const
+{
+ return d->startTimeData[index].duration;
+}
+
+qint64 QmlProfilerTimelineModelProxy::getStartTime(int index) const
+{
+ return d->startTimeData[index].startTime;
+}
+
+qint64 QmlProfilerTimelineModelProxy::getEndTime(int index) const
+{
+ return d->startTimeData[index].startTime + d->startTimeData[index].duration;
+}
+
+int QmlProfilerTimelineModelProxy::getEventId(int index) const
+{
+ return d->startTimeData[index].eventId;
+}
+
+int QmlProfilerTimelineModelProxy::getBindingLoopDest(int index) const
+{
+ return d->startTimeData[index].bindingLoopHead;
+}
+
+const QVariantList QmlProfilerTimelineModelProxy::getLabelsForCategory(int category) const
+{
+ QVariantList result;
+
+ if (d->categorySpan.count() > category && d->categorySpan[category].expanded) {
+ int eventCount = d->eventDict.count();
+ for (int i = 0; i < eventCount; i++) {
+ if (d->eventDict[i].eventType == category) {
+ QVariantMap element;
+ element.insert(QLatin1String("displayName"), QVariant(d->eventDict[i].displayName));
+ element.insert(QLatin1String("description"), QVariant(d->eventDict[i].details));
+ element.insert(QLatin1String("id"), QVariant(d->eventDict[i].eventId));
+ result << element;
+ }
+ }
+ }
+
+ return result;
+}
+
+QString QmlProfilerTimelineModelProxy::QmlProfilerTimelineModelProxyPrivate::displayTime(double time)
+{
+ if (time < 1e6)
+ return QString::number(time/1e3,'f',3) + trUtf8(" \xc2\xb5s");
+ if (time < 1e9)
+ return QString::number(time/1e6,'f',3) + tr(" ms");
+
+ return QString::number(time/1e9,'f',3) + tr(" s");
+}
+
+const QVariantList QmlProfilerTimelineModelProxy::getEventDetails(int index) const
+{
+ QVariantList result;
+ int eventId = getEventId(index);
+
+ {
+ QVariantMap valuePair;
+ valuePair.insert(tr("title"), QVariant(categoryLabel(d->eventDict[eventId].eventType)));
+ result << valuePair;
+ }
+
+ // duration
+ {
+ QVariantMap valuePair;
+ valuePair.insert(tr("Duration:"), QVariant(d->displayTime(d->startTimeData[index].duration)));
+ result << valuePair;
+ }
+
+ // details
+ {
+ QVariantMap valuePair;
+ QString detailsString = d->eventDict[eventId].details;
+ if (detailsString.length() > 40)
+ detailsString = detailsString.left(40) + QLatin1String("...");
+ valuePair.insert(tr("Details:"), QVariant(detailsString));
+ result << valuePair;
+ }
+
+ // location
+ {
+ QVariantMap valuePair;
+ valuePair.insert(tr("Location:"), QVariant(d->eventDict[eventId].displayName));
+ result << valuePair;
+ }
+
+ // isbindingloop
+ {}
+
+
+ return result;
+}
+
+}
+}
diff --git a/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.h b/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.h
new file mode 100644
index 0000000000..c5448dee57
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.h
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+
+#ifndef QMLPROFILERTIMELINEMODELPROXY_H
+#define QMLPROFILERTIMELINEMODELPROXY_H
+
+#include <QObject>
+#include <qmldebug/qmlprofilereventtypes.h>
+#include <qmldebug/qmlprofilereventlocation.h>
+//#include <QHash>
+//#include <QVector>
+#include <QVariantList>
+//#include <QVariantMap>
+
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerModelManager;
+
+class QmlProfilerTimelineModelProxy : public QObject
+{
+ Q_PROPERTY(bool empty READ empty NOTIFY emptyChanged)
+
+ Q_OBJECT
+public:
+ struct QmlRangeEventData
+ {
+ QString displayName;
+// QString eventHashStr;
+ QString details;
+ QmlDebug::QmlEventLocation location;
+ QmlDebug::QmlEventType eventType;
+
+ int eventId; // separate
+ };
+
+ struct QmlRangeEventStartInstance {
+ qint64 startTime;
+ qint64 duration;
+
+// int endTimeIndex;
+
+ // not-expanded, per type
+ int displayRowExpanded;
+ int displayRowCollapsed;
+ int baseEventIndex; // used by findfirstindex
+
+
+// QmlRangeEventData *statsInfo;
+ int eventId;
+
+ // animation-related data
+// int frameRate;
+// int animationCount;
+
+ int bindingLoopHead;
+ };
+
+ struct QmlRangeEventEndInstance {
+ int startTimeIndex;
+ qint64 endTime;
+ };
+
+// struct QmlRangedEvent {
+// int bindingType; // TODO: only makes sense for bindings!
+// QString displayName;
+// QString eventHashStr;
+// QString details;
+// QmlDebug::QmlEventLocation location;
+// QmlDebug::QmlEventType eventType;
+// //int eventType;
+
+// qint64 duration;
+// };
+
+ QmlProfilerTimelineModelProxy(QmlProfilerModelManager *modelManager, QObject *parent = 0);
+ ~QmlProfilerTimelineModelProxy();
+
+ const QVector<QmlRangeEventStartInstance> getData() const;
+ const QVector<QmlRangeEventStartInstance> getData(qint64 fromTime, qint64 toTime) const;
+ void loadData();
+ Q_INVOKABLE int count() const;
+ void clear();
+
+
+// QML interface
+ bool empty() const;
+
+ Q_INVOKABLE qint64 lastTimeMark() const;
+ Q_INVOKABLE qint64 traceStartTime() const;
+ Q_INVOKABLE qint64 traceEndTime() const;
+ Q_INVOKABLE qint64 traceDuration() const;
+ Q_INVOKABLE int getState() const;
+
+ Q_INVOKABLE void setExpanded(int category, bool expanded);
+ Q_INVOKABLE int categoryDepth(int categoryIndex) const;
+ Q_INVOKABLE int categoryCount() const;
+ Q_INVOKABLE const QString categoryLabel(int categoryIndex) const;
+
+ int findFirstIndex(qint64 startTime) const;
+ int findFirstIndexNoParents(qint64 startTime) const;
+ int findLastIndex(qint64 endTime) const;
+
+ int getEventType(int index) const;
+ int getEventRow(int index) const;
+ Q_INVOKABLE qint64 getDuration(int index) const;
+ Q_INVOKABLE qint64 getStartTime(int index) const;
+ Q_INVOKABLE qint64 getEndTime(int index) const;
+ Q_INVOKABLE int getEventId(int index) const;
+ int getBindingLoopDest(int index) const;
+
+ const QmlProfilerTimelineModelProxy::QmlRangeEventData &getRangeEventData(int index) const;
+ Q_INVOKABLE const QVariantList getLabelsForCategory(int category) const;
+
+ Q_INVOKABLE const QVariantList getEventDetails(int index) const;
+
+
+signals:
+ void countChanged();
+ void dataAvailable();
+ void stateChanged();
+ void emptyChanged();
+ void expandedChanged();
+
+private slots:
+ void dataChanged();
+
+private:
+ class QmlProfilerTimelineModelProxyPrivate;
+ QmlProfilerTimelineModelProxyPrivate *d;
+
+};
+
+}
+}
+
+#endif
diff --git a/plugins/qmlprofiler/qmlprofilertool.cpp b/plugins/qmlprofiler/qmlprofilertool.cpp
new file mode 100644
index 0000000000..2e64b9500c
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilertool.cpp
@@ -0,0 +1,734 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilertool.h"
+#include "qmlprofilerstatemanager.h"
+#include "qmlprofilerengine.h"
+#include "qmlprofilerconstants.h"
+#include "qmlprofilerattachdialog.h"
+#include "qmlprofilerviewmanager.h"
+#include "qmlprofilerclientmanager.h"
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilerdetailsrewriter.h"
+#include "timelinerenderer.h"
+
+#include <analyzerbase/analyzermanager.h>
+#include <analyzerbase/analyzerruncontrol.h>
+
+#include "canvas/qdeclarativecontext2d_p.h"
+#include "canvas/qmlprofilercanvas.h"
+
+#include <qmlprojectmanager/qmlprojectrunconfiguration.h>
+#include <utils/fancymainwindow.h>
+#include <utils/fileinprojectfinder.h>
+#include <utils/qtcassert.h>
+#include <projectexplorer/environmentaspect.h>
+#include <projectexplorer/projectexplorer.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/target.h>
+#include <projectexplorer/session.h>
+#include <projectexplorer/kitinformation.h>
+#include <projectexplorer/localapplicationrunconfiguration.h>
+#include <texteditor/itexteditor.h>
+
+#include <android/androidconstants.h>
+
+#include <coreplugin/coreconstants.h>
+#include <coreplugin/editormanager/editormanager.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/messagemanager.h>
+#include <coreplugin/helpmanager.h>
+#include <coreplugin/modemanager.h>
+#include <coreplugin/imode.h>
+#include <coreplugin/actionmanager/command.h>
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/actionmanager/actioncontainer.h>
+
+#include <debugger/debuggerrunconfigurationaspect.h>
+
+#include <qtsupport/qtkitinformation.h>
+
+#include <QApplication>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QToolButton>
+#include <QMessageBox>
+#include <QFileDialog>
+#include <QMenu>
+#include <QTimer>
+#include <QTime>
+#include <QTcpServer>
+
+using namespace Core;
+using namespace Core::Constants;
+using namespace Analyzer;
+using namespace Analyzer::Constants;
+using namespace QmlProfiler::Internal;
+using namespace QmlProfiler::Constants;
+using namespace QmlDebug;
+using namespace ProjectExplorer;
+using namespace QmlProjectManager;
+
+class QmlProfilerTool::QmlProfilerToolPrivate
+{
+public:
+ QmlProfilerToolPrivate(QmlProfilerTool *qq) : q(qq) {}
+ ~QmlProfilerToolPrivate() {}
+
+ QmlProfilerTool *q;
+
+ QmlProfilerStateManager *m_profilerState;
+ QmlProfilerClientManager *m_profilerConnections;
+ QmlProfilerModelManager *m_profilerModelManager;
+
+ QmlProfilerViewManager *m_viewContainer;
+ Utils::FileInProjectFinder m_projectFinder;
+ RunConfiguration *m_runConfiguration;
+ QToolButton *m_recordButton;
+ QToolButton *m_clearButton;
+
+ // elapsed time display
+ QTimer m_recordingTimer;
+ QTime m_recordingElapsedTime;
+ QLabel *m_timeLabel;
+
+ // save and load actions
+ QAction *m_saveQmlTrace;
+ QAction *m_loadQmlTrace;
+};
+
+QmlProfilerTool::QmlProfilerTool(QObject *parent)
+ : IAnalyzerTool(parent), d(new QmlProfilerToolPrivate(this))
+{
+ setObjectName(QLatin1String("QmlProfilerTool"));
+
+ d->m_profilerState = 0;
+ d->m_viewContainer = 0;
+ d->m_runConfiguration = 0;
+
+ qmlRegisterType<QmlProfilerCanvas>("Monitor", 1, 0, "Canvas2D");
+ qmlRegisterType<Context2D>();
+ qmlRegisterType<CanvasGradient>();
+ qmlRegisterType<TimelineRenderer>("Monitor", 1, 0,"TimelineRenderer");
+
+ d->m_profilerState = new QmlProfilerStateManager(this);
+ connect(d->m_profilerState, SIGNAL(stateChanged()), this, SLOT(profilerStateChanged()));
+ connect(d->m_profilerState, SIGNAL(clientRecordingChanged()), this, SLOT(clientRecordingChanged()));
+ connect(d->m_profilerState, SIGNAL(serverRecordingChanged()), this, SLOT(serverRecordingChanged()));
+
+ d->m_profilerConnections = new QmlProfilerClientManager(this);
+ d->m_profilerConnections->registerProfilerStateManager(d->m_profilerState);
+ connect(d->m_profilerConnections, SIGNAL(connectionClosed()), this, SLOT(clientsDisconnected()));
+
+ d->m_profilerModelManager = new QmlProfilerModelManager(&d->m_projectFinder, this);
+ connect(d->m_profilerModelManager, SIGNAL(stateChanged()), this, SLOT(profilerDataModelStateChanged()));
+ connect(d->m_profilerModelManager, SIGNAL(error(QString)), this, SLOT(showErrorDialog(QString)));
+ connect(d->m_profilerConnections,
+ SIGNAL(addRangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)),
+ d->m_profilerModelManager,
+ SLOT(addRangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)));
+ connect(d->m_profilerConnections,
+ SIGNAL(addV8Event(int,QString,QString,int,double,double)),
+ d->m_profilerModelManager,
+ SLOT(addV8Event(int,QString,QString,int,double,double)));
+#ifdef PROFILER_FRAMEEVENTS
+ connect(d->m_profilerConnections, SIGNAL(addFrameEvent(qint64,int,int)), d->m_profilerModelManager, SLOT(addFrameEvent(qint64,int,int)));
+#endif
+ connect(d->m_profilerConnections, SIGNAL(traceStarted(qint64)), d->m_profilerModelManager->traceTime(), SLOT(setStartTime(qint64)));
+ connect(d->m_profilerConnections, SIGNAL(traceFinished(qint64)), d->m_profilerModelManager->traceTime(), SLOT(setEndTime(qint64)));
+ connect(d->m_profilerConnections, SIGNAL(dataReadyForProcessing()), d->m_profilerModelManager, SLOT(complete()));
+
+ Command *command = 0;
+ const Context globalContext(C_GLOBAL);
+
+ ActionContainer *menu = Core::ActionManager::actionContainer(M_DEBUG_ANALYZER);
+ ActionContainer *options = Core::ActionManager::createMenu(M_DEBUG_ANALYZER_QML_OPTIONS);
+ options->menu()->setTitle(tr("QML Profiler Options"));
+ menu->addMenu(options, G_ANALYZER_OPTIONS);
+ options->menu()->setEnabled(true);
+
+ QAction *act = d->m_loadQmlTrace = new QAction(tr("Load QML Trace"), options);
+ command = Core::ActionManager::registerAction(act, "Analyzer.Menu.StartAnalyzer.QMLProfilerOptions.LoadQMLTrace", globalContext);
+ connect(act, SIGNAL(triggered()), this, SLOT(showLoadDialog()));
+ options->addAction(command);
+
+ act = d->m_saveQmlTrace = new QAction(tr("Save QML Trace"), options);
+ d->m_saveQmlTrace->setEnabled(false);
+ command = Core::ActionManager::registerAction(act, "Analyzer.Menu.StartAnalyzer.QMLProfilerOptions.SaveQMLTrace", globalContext);
+ connect(act, SIGNAL(triggered()), this, SLOT(showSaveDialog()));
+ options->addAction(command);
+
+ d->m_recordingTimer.setInterval(100);
+ connect(&d->m_recordingTimer, SIGNAL(timeout()), this, SLOT(updateTimeDisplay()));
+}
+
+QmlProfilerTool::~QmlProfilerTool()
+{
+ delete d;
+}
+
+Core::Id QmlProfilerTool::id() const
+{
+ return Core::Id("QmlProfiler");
+}
+
+RunMode QmlProfilerTool::runMode() const
+{
+ return QmlProfilerRunMode;
+}
+
+QString QmlProfilerTool::displayName() const
+{
+ return tr("QML Profiler");
+}
+
+QString QmlProfilerTool::description() const
+{
+ return tr("The QML Profiler can be used to find performance bottlenecks in "
+ "applications using QML.");
+}
+
+IAnalyzerTool::ToolMode QmlProfilerTool::toolMode() const
+{
+ return AnyMode;
+}
+
+IAnalyzerEngine *QmlProfilerTool::createEngine(const AnalyzerStartParameters &sp,
+ RunConfiguration *runConfiguration)
+{
+ QmlProfilerEngine *engine = new QmlProfilerEngine(this, sp, runConfiguration);
+
+ engine->registerProfilerStateManager(d->m_profilerState);
+
+ bool isTcpConnection = true;
+
+ if (runConfiguration) {
+ // Check minimum Qt Version. We cannot really be sure what the Qt version
+ // at runtime is, but guess that the active build configuraiton has been used.
+ QtSupport::QtVersionNumber minimumVersion(4, 7, 4);
+ QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(runConfiguration->target()->kit());
+ if (version) {
+ if (version->isValid() && version->qtVersion() < minimumVersion) {
+ int result = QMessageBox::warning(QApplication::activeWindow(), tr("QML Profiler"),
+ tr("The QML profiler requires Qt 4.7.4 or newer.\n"
+ "The Qt version configured in your active build configuration is too old.\n"
+ "Do you want to continue?"), QMessageBox::Yes, QMessageBox::No);
+ if (result == QMessageBox::No)
+ return 0;
+ }
+ }
+ }
+
+ // FIXME: Check that there's something sensible in sp.connParams
+ if (isTcpConnection)
+ d->m_profilerConnections->setTcpConnection(sp.analyzerHost, sp.analyzerPort);
+
+ d->m_runConfiguration = runConfiguration;
+
+ //
+ // Initialize m_projectFinder
+ //
+
+ QString projectDirectory;
+ if (d->m_runConfiguration) {
+ Project *project = d->m_runConfiguration->target()->project();
+ projectDirectory = project->projectDirectory();
+ }
+
+ populateFileFinder(projectDirectory, sp.sysroot);
+
+ connect(engine, SIGNAL(processRunning(quint16)), d->m_profilerConnections, SLOT(connectClient(quint16)));
+ connect(d->m_profilerConnections, SIGNAL(connectionFailed()), engine, SLOT(cancelProcess()));
+
+ return engine;
+}
+
+bool QmlProfilerTool::canRun(RunConfiguration *runConfiguration, RunMode mode) const
+{
+ if (qobject_cast<QmlProjectRunConfiguration *>(runConfiguration)
+ || qobject_cast<LocalApplicationRunConfiguration *>(runConfiguration))
+ return mode == runMode();
+ return false;
+}
+
+static QString sysroot(RunConfiguration *runConfig)
+{
+ QTC_ASSERT(runConfig, return QString());
+ ProjectExplorer::Kit *k = runConfig->target()->kit();
+ if (k && ProjectExplorer::SysRootKitInformation::hasSysRoot(k))
+ return ProjectExplorer::SysRootKitInformation::sysRoot(runConfig->target()->kit()).toString();
+ return QString();
+}
+
+AnalyzerStartParameters QmlProfilerTool::createStartParameters(RunConfiguration *runConfiguration, RunMode mode) const
+{
+ Q_UNUSED(mode);
+
+ AnalyzerStartParameters sp;
+ ProjectExplorer::EnvironmentAspect *environment
+ = runConfiguration->extraAspect<ProjectExplorer::EnvironmentAspect>();
+ Debugger::DebuggerRunConfigurationAspect *debugger
+ = runConfiguration->extraAspect<Debugger::DebuggerRunConfigurationAspect>();
+ QTC_ASSERT(debugger, return sp);
+
+ // FIXME: This is only used to communicate the connParams settings.
+ if (QmlProjectRunConfiguration *rc1 =
+ qobject_cast<QmlProjectRunConfiguration *>(runConfiguration)) {
+ // This is a "plain" .qmlproject.
+ if (environment)
+ sp.environment = environment->environment();
+ sp.workingDirectory = rc1->workingDirectory();
+ sp.debuggee = rc1->observerPath();
+ sp.debuggeeArgs = rc1->viewerArguments();
+ sp.displayName = rc1->displayName();
+ } else if (LocalApplicationRunConfiguration *rc2 =
+ qobject_cast<LocalApplicationRunConfiguration *>(runConfiguration)) {
+ if (environment)
+ sp.environment = environment->environment();
+ sp.workingDirectory = rc2->workingDirectory();
+ sp.debuggee = rc2->executable();
+ sp.debuggeeArgs = rc2->commandLineArguments();
+ sp.displayName = rc2->displayName();
+ } else {
+ // What could that be?
+ QTC_ASSERT(false, return sp);
+ }
+ const ProjectExplorer::IDevice::ConstPtr device =
+ ProjectExplorer::DeviceKitInformation::device(runConfiguration->target()->kit());
+ if (device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) {
+ QTcpServer server;
+ if (!server.listen(QHostAddress::LocalHost) && !server.listen(QHostAddress::LocalHostIPv6)) {
+ qWarning() << "Cannot open port on host for QML profiling.";
+ return sp;
+ }
+ sp.analyzerHost = server.serverAddress().toString();
+ sp.analyzerPort = server.serverPort();
+ }
+ sp.startMode = StartQml;
+ return sp;
+}
+
+QWidget *QmlProfilerTool::createWidgets()
+{
+ QTC_ASSERT(!d->m_viewContainer, return 0);
+
+
+ d->m_viewContainer = new QmlProfilerViewManager(this,
+ this,
+ d->m_profilerModelManager,
+ d->m_profilerState);
+ connect(d->m_viewContainer, SIGNAL(gotoSourceLocation(QString,int,int)),
+ this, SLOT(gotoSourceLocation(QString,int,int)));
+
+
+ //
+ // Toolbar
+ //
+ QWidget *toolbarWidget = new QWidget;
+ toolbarWidget->setObjectName(QLatin1String("QmlProfilerToolBarWidget"));
+
+ QHBoxLayout *layout = new QHBoxLayout;
+ layout->setMargin(0);
+ layout->setSpacing(0);
+
+ d->m_recordButton = new QToolButton(toolbarWidget);
+ d->m_recordButton->setCheckable(true);
+
+ connect(d->m_recordButton,SIGNAL(clicked(bool)), this, SLOT(recordingButtonChanged(bool)));
+ d->m_recordButton->setChecked(true);
+ setRecording(d->m_profilerState->clientRecording());
+ layout->addWidget(d->m_recordButton);
+
+ d->m_clearButton = new QToolButton(toolbarWidget);
+ d->m_clearButton->setIcon(QIcon(QLatin1String(":/qmlprofiler/clean_pane_small.png")));
+ d->m_clearButton->setToolTip(tr("Discard data"));
+
+ connect(d->m_clearButton,SIGNAL(clicked()), this, SLOT(clearData()));
+
+ layout->addWidget(d->m_clearButton);
+
+ d->m_timeLabel = new QLabel();
+ QPalette palette = d->m_timeLabel->palette();
+ palette.setColor(QPalette::WindowText, Qt::white);
+ d->m_timeLabel->setPalette(palette);
+ d->m_timeLabel->setIndent(10);
+ updateTimeDisplay();
+ layout->addWidget(d->m_timeLabel);
+
+ toolbarWidget->setLayout(layout);
+
+ // When the widgets are requested we assume that the session data
+ // is available, then we can populate the file finder
+ populateFileFinder();
+
+ return toolbarWidget;
+}
+
+void QmlProfilerTool::populateFileFinder(QString projectDirectory, QString activeSysroot)
+{
+ // Initialize filefinder with some sensible default
+ QStringList sourceFiles;
+ SessionManager *sessionManager = ProjectExplorerPlugin::instance()->session();
+ QList<Project *> projects = sessionManager->projects();
+ if (Project *startupProject = ProjectExplorerPlugin::instance()->startupProject()) {
+ // startup project first
+ projects.removeOne(ProjectExplorerPlugin::instance()->startupProject());
+ projects.insert(0, startupProject);
+ }
+ foreach (Project *project, projects)
+ sourceFiles << project->files(Project::ExcludeGeneratedFiles);
+
+ if (!projects.isEmpty()) {
+ if (projectDirectory.isEmpty())
+ projectDirectory = projects.first()->projectDirectory();
+
+ if (activeSysroot.isEmpty()) {
+ if (Target *target = projects.first()->activeTarget())
+ if (RunConfiguration *rc = target->activeRunConfiguration())
+ activeSysroot = sysroot(rc);
+ }
+ }
+
+ d->m_projectFinder.setProjectDirectory(projectDirectory);
+ d->m_projectFinder.setProjectFiles(sourceFiles);
+ d->m_projectFinder.setSysroot(activeSysroot);
+}
+
+void QmlProfilerTool::recordingButtonChanged(bool recording)
+{
+ d->m_profilerState->setClientRecording(recording);
+}
+
+void QmlProfilerTool::setRecording(bool recording)
+{
+ // update display
+ d->m_recordButton->setToolTip( recording ? tr("Disable profiling") : tr("Enable profiling"));
+ d->m_recordButton->setIcon(QIcon(recording ? QLatin1String(":/qmlprofiler/recordOn.png") :
+ QLatin1String(":/qmlprofiler/recordOff.png")));
+
+ d->m_recordButton->setChecked(recording);
+
+ // manage timer
+ if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
+ if (recording) {
+ d->m_recordingTimer.start();
+ d->m_recordingElapsedTime.start();
+ } else {
+ d->m_recordingTimer.stop();
+ }
+ }
+}
+
+void QmlProfilerTool::gotoSourceLocation(const QString &fileUrl, int lineNumber, int columnNumber)
+{
+ if (lineNumber < 0 || fileUrl.isEmpty())
+ return;
+
+ const QString projectFileName = d->m_projectFinder.findFile(fileUrl);
+
+ QFileInfo fileInfo(projectFileName);
+ if (!fileInfo.exists() || !fileInfo.isReadable())
+ return;
+
+ IEditor *editor = EditorManager::openEditor(projectFileName);
+ TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor*>(editor);
+
+ if (textEditor) {
+ EditorManager::instance()->addCurrentPositionToNavigationHistory();
+ // textEditor counts columns starting with 0, but the ASTs store the
+ // location starting with 1, therefore the -1 in the call to gotoLine
+ textEditor->gotoLine(lineNumber, columnNumber - 1);
+ textEditor->widget()->setFocus();
+ }
+}
+
+void QmlProfilerTool::updateTimeDisplay()
+{
+ double seconds = 0;
+ if (d->m_profilerState->serverRecording() &&
+ d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
+ seconds = d->m_recordingElapsedTime.elapsed() / 1000.0;
+ } else if (d->m_profilerModelManager->state() != QmlProfilerDataState::Empty ) {
+ //seconds = d->m_profilerModelManager->traceDuration() / 1.0e9;
+ seconds = d->m_profilerModelManager->traceTime()->duration() / 1.0e9;
+ }
+ QString timeString = QString::number(seconds,'f',1);
+ QString profilerTimeStr = QmlProfilerTool::tr("%1 s").arg(timeString, 6);
+ d->m_timeLabel->setText(tr("Elapsed: %1").arg(profilerTimeStr));
+}
+
+void QmlProfilerTool::clearData()
+{
+ d->m_profilerModelManager->clear();
+ d->m_profilerConnections->discardPendingData();
+}
+
+void QmlProfilerTool::clearDisplay()
+{
+ d->m_profilerConnections->clearBufferedData();
+ d->m_viewContainer->clear();
+ updateTimeDisplay();
+}
+
+static void startRemoteTool(IAnalyzerTool *tool, StartMode mode)
+{
+ Q_UNUSED(tool);
+
+ Id kitId;
+ quint16 port;
+ Kit *kit = 0;
+
+ {
+ QSettings *settings = ICore::settings();
+
+ kitId = Id::fromSetting(settings->value(QLatin1String("AnalyzerQmlAttachDialog/kitId")));
+ port = settings->value(QLatin1String("AnalyzerQmlAttachDialog/port"), 3768).toUInt();
+
+ QmlProfilerAttachDialog dialog;
+
+ dialog.setKitId(kitId);
+ dialog.setPort(port);
+
+ if (dialog.exec() != QDialog::Accepted)
+ return;
+
+ kit = dialog.kit();
+ port = dialog.port();
+
+ settings->setValue(QLatin1String("AnalyzerQmlAttachDialog/kitId"), kit->id().toSetting());
+ settings->setValue(QLatin1String("AnalyzerQmlAttachDialog/port"), port);
+ }
+
+ AnalyzerStartParameters sp;
+ sp.toolId = tool->id();
+ sp.startMode = mode;
+
+ IDevice::ConstPtr device = DeviceKitInformation::device(kit);
+ if (device) {
+ sp.connParams = device->sshParameters();
+ if (device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE
+ || device->type() == Android::Constants::ANDROID_DEVICE_TYPE) {
+ sp.analyzerHost = QLatin1String("localhost");
+ } else {
+ sp.analyzerHost = sp.connParams.host;
+ }
+ }
+ sp.sysroot = SysRootKitInformation::sysRoot(kit).toString();
+ sp.analyzerPort = port;
+
+ AnalyzerRunControl *rc = new AnalyzerRunControl(tool, sp, 0);
+ QObject::connect(AnalyzerManager::stopAction(), SIGNAL(triggered()), rc, SLOT(stopIt()));
+
+ ProjectExplorerPlugin::instance()->startRunControl(rc, tool->runMode());
+}
+
+void QmlProfilerTool::startTool(StartMode mode)
+{
+ using namespace ProjectExplorer;
+
+ // Make sure mode is shown.
+ AnalyzerManager::showMode();
+
+ if (mode == StartLocal) {
+ ProjectExplorerPlugin *pe = ProjectExplorerPlugin::instance();
+ // ### not sure if we're supposed to check if the RunConFiguration isEnabled
+ Project *pro = pe->startupProject();
+ pe->runProject(pro, runMode());
+ } else if (mode == StartRemote) {
+ startRemoteTool(this, mode);
+ }
+}
+
+void QmlProfilerTool::logStatus(const QString &msg)
+{
+ MessageManager *messageManager = MessageManager::instance();
+ messageManager->printToOutputPane(msg, Core::MessageManager::Flash);
+}
+
+void QmlProfilerTool::logError(const QString &msg)
+{
+ MessageManager *messageManager = MessageManager::instance();
+ messageManager->printToOutputPane(msg, Core::MessageManager::NoModeSwitch);
+}
+
+void QmlProfilerTool::showErrorDialog(const QString &error)
+{
+ QMessageBox *errorDialog = new QMessageBox(Core::ICore::mainWindow());
+ errorDialog->setIcon(QMessageBox::Warning);
+ errorDialog->setWindowTitle(tr("QML Profiler"));
+ errorDialog->setText(error);
+ errorDialog->setStandardButtons(QMessageBox::Ok);
+ errorDialog->setDefaultButton(QMessageBox::Ok);
+ errorDialog->setModal(false);
+ errorDialog->show();
+}
+
+void QmlProfilerTool::showSaveOption()
+{
+ d->m_saveQmlTrace->setEnabled(!d->m_profilerModelManager->isEmpty());
+}
+
+void QmlProfilerTool::showSaveDialog()
+{
+ QString filename = QFileDialog::getSaveFileName(Core::ICore::mainWindow(), tr("Save QML Trace"), QString(),
+ tr("QML traces (*%1)").arg(QLatin1String(TraceFileExtension)));
+ if (!filename.isEmpty()) {
+ if (!filename.endsWith(QLatin1String(TraceFileExtension)))
+ filename += QLatin1String(TraceFileExtension);
+ d->m_profilerModelManager->save(filename);
+ }
+}
+
+void QmlProfilerTool::showLoadDialog()
+{
+ if (ModeManager::currentMode()->id() != MODE_ANALYZE)
+ AnalyzerManager::showMode();
+
+ if (AnalyzerManager::currentSelectedTool() != this)
+ AnalyzerManager::selectTool(this, StartRemote);
+
+ QString filename = QFileDialog::getOpenFileName(Core::ICore::mainWindow(), tr("Load QML Trace"), QString(),
+ tr("QML traces (*%1)").arg(QLatin1String(TraceFileExtension)));
+
+ if (!filename.isEmpty()) {
+ // delayed load (prevent graphical artifacts due to long load time)
+ d->m_profilerModelManager->setFilename(filename);
+ QTimer::singleShot(100, d->m_profilerModelManager, SLOT(load()));
+ }
+}
+
+void QmlProfilerTool::clientsDisconnected()
+{
+ // If the application stopped by itself, check if we have all the data
+ if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppDying) {
+ if (d->m_profilerModelManager->state() == QmlProfilerDataState::AcquiringData)
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppKilled);
+ else
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStopped);
+
+ // ... and return to the "base" state
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
+ }
+ // If the connection is closed while the app is still running, no special action is needed
+}
+
+void QmlProfilerTool::profilerDataModelStateChanged()
+{
+ switch (d->m_profilerModelManager->state()) {
+ case QmlProfilerDataState::Empty :
+ clearDisplay();
+ break;
+ case QmlProfilerDataState::AcquiringData :
+ case QmlProfilerDataState::ProcessingData :
+ // nothing to be done for these two
+ break;
+ case QmlProfilerDataState::Done :
+ if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppStopRequested)
+ d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppReadyToStop);
+ showSaveOption();
+ updateTimeDisplay();
+ break;
+ default:
+ break;
+ }
+}
+
+QList <QAction *> QmlProfilerTool::profilerContextMenuActions() const
+{
+ QList <QAction *> commonActions;
+ commonActions << d->m_loadQmlTrace << d->m_saveQmlTrace;
+ return commonActions;
+}
+
+void QmlProfilerTool::showNonmodalWarning(const QString &warningMsg)
+{
+ QMessageBox *noExecWarning = new QMessageBox(Core::ICore::mainWindow());
+ noExecWarning->setIcon(QMessageBox::Warning);
+ noExecWarning->setWindowTitle(tr("QML Profiler"));
+ noExecWarning->setText(warningMsg);
+ noExecWarning->setStandardButtons(QMessageBox::Ok);
+ noExecWarning->setDefaultButton(QMessageBox::Ok);
+ noExecWarning->setModal(false);
+ noExecWarning->show();
+}
+
+QMessageBox *QmlProfilerTool::requestMessageBox()
+{
+ return new QMessageBox(Core::ICore::mainWindow());
+}
+
+void QmlProfilerTool::handleHelpRequest(const QString &link)
+{
+ HelpManager *helpManager = HelpManager::instance();
+ helpManager->handleHelpRequest(link);
+}
+
+void QmlProfilerTool::profilerStateChanged()
+{
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppDying : {
+ // If already disconnected when dying, check again that all data was read
+ if (!d->m_profilerConnections->isConnected())
+ QTimer::singleShot(0, this, SLOT(clientsDisconnected()));
+ break;
+ }
+ case QmlProfilerStateManager::AppKilled : {
+ showNonmodalWarning(tr("Application finished before loading profiled data.\nPlease use the stop button instead."));
+ d->m_profilerModelManager->clear();
+ break;
+ }
+ case QmlProfilerStateManager::Idle :
+ // when the app finishes, set recording display to client status
+ setRecording(d->m_profilerState->clientRecording());
+ break;
+ default:
+ // no special action needed for other states
+ break;
+ }
+}
+
+void QmlProfilerTool::clientRecordingChanged()
+{
+ // if application is running, display server record changes
+ // if application is stopped, display client record changes
+ if (d->m_profilerState->currentState() != QmlProfilerStateManager::AppRunning)
+ setRecording(d->m_profilerState->clientRecording());
+}
+
+void QmlProfilerTool::serverRecordingChanged()
+{
+ if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
+ setRecording(d->m_profilerState->serverRecording());
+ // clear the old data each time we start a new profiling session
+ if (d->m_profilerState->serverRecording()) {
+ clearData();
+ d->m_profilerModelManager->prepareForWriting();
+ }
+ }
+}
diff --git a/plugins/qmlprofiler/qmlprofilertool.h b/plugins/qmlprofiler/qmlprofilertool.h
new file mode 100644
index 0000000000..412cb6a87f
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilertool.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERTOOL_H
+#define QMLPROFILERTOOL_H
+
+#include <analyzerbase/ianalyzertool.h>
+#include <analyzerbase/ianalyzerengine.h>
+
+QT_BEGIN_NAMESPACE
+class QMessageBox;
+QT_END_NAMESPACE
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerTool : public Analyzer::IAnalyzerTool
+{
+ Q_OBJECT
+
+public:
+ explicit QmlProfilerTool(QObject *parent);
+ ~QmlProfilerTool();
+
+ Core::Id id() const;
+ ProjectExplorer::RunMode runMode() const;
+ QString displayName() const;
+ QString description() const;
+ ToolMode toolMode() const;
+
+ void extensionsInitialized() {}
+
+ Analyzer::IAnalyzerEngine *createEngine(const Analyzer::AnalyzerStartParameters &sp,
+ ProjectExplorer::RunConfiguration *runConfiguration = 0);
+
+ bool canRun(ProjectExplorer::RunConfiguration *runConfiguration,
+ ProjectExplorer::RunMode mode) const;
+
+ Analyzer::AnalyzerStartParameters createStartParameters(
+ ProjectExplorer::RunConfiguration *runConfiguration,
+ ProjectExplorer::RunMode mode) const;
+
+ QWidget *createWidgets();
+ void startTool(Analyzer::StartMode mode);
+
+ QList <QAction *> profilerContextMenuActions() const;
+
+ // display dialogs / log output
+ static QMessageBox *requestMessageBox();
+ static void handleHelpRequest(const QString &link);
+ static void logStatus(const QString &msg);
+ static void logError(const QString &msg);
+ static void showNonmodalWarning(const QString &warningMsg);
+
+public slots:
+ void profilerStateChanged();
+ void clientRecordingChanged();
+ void serverRecordingChanged();
+ void clientsDisconnected();
+
+ void recordingButtonChanged(bool recording);
+ void setRecording(bool recording);
+
+ void gotoSourceLocation(const QString &fileUrl, int lineNumber, int columnNumber);
+
+private slots:
+ void clearData();
+ void showErrorDialog(const QString &error);
+ void profilerDataModelStateChanged();
+ void updateTimeDisplay();
+
+ void showSaveOption();
+ void showSaveDialog();
+ void showLoadDialog();
+
+private:
+ void clearDisplay();
+ void populateFileFinder(QString projectDirectory = QString(), QString activeSysroot = QString());
+
+ class QmlProfilerToolPrivate;
+ QmlProfilerToolPrivate *d;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILERTOOL_H
diff --git a/plugins/qmlprofiler/qmlprofilertracefile.cpp b/plugins/qmlprofiler/qmlprofilertracefile.cpp
new file mode 100644
index 0000000000..4ad00ce02c
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilertracefile.cpp
@@ -0,0 +1,485 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilertracefile.h"
+
+#include <utils/qtcassert.h>
+
+#include <QIODevice>
+#include <QStringList>
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+#include <QDebug>
+
+// import QmlEventType, QmlBindingType enums, QmlEventLocation
+using namespace QmlDebug;
+
+
+const char PROFILER_FILE_VERSION[] = "1.02";
+
+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";
+
+#define _(X) QLatin1String(X)
+
+
+//
+// "be strict in your output but tolerant in your inputs"
+//
+
+namespace QmlProfiler {
+namespace Internal {
+
+static QmlEventType qmlEventTypeAsEnum(const QString &typeString)
+{
+ if (typeString == _(TYPE_PAINTING_STR)) {
+ return Painting;
+ } else if (typeString == _(TYPE_COMPILING_STR)) {
+ return Compiling;
+ } else if (typeString == _(TYPE_CREATING_STR)) {
+ return Creating;
+ } else if (typeString == _(TYPE_BINDING_STR)) {
+ return Binding;
+ } else if (typeString == _(TYPE_HANDLINGSIGNAL_STR)) {
+ return HandlingSignal;
+ } else {
+ bool isNumber = false;
+ int type = typeString.toUInt(&isNumber);
+ if (isNumber)
+ return (QmlEventType)type;
+ else
+ return MaximumQmlEventType;
+ }
+}
+
+static QString qmlEventTypeAsString(QmlEventType typeEnum)
+{
+ switch (typeEnum) {
+ case Painting:
+ return _(TYPE_PAINTING_STR);
+ break;
+ case Compiling:
+ return _(TYPE_COMPILING_STR);
+ break;
+ case Creating:
+ return _(TYPE_CREATING_STR);
+ break;
+ case Binding:
+ return _(TYPE_BINDING_STR);
+ break;
+ case HandlingSignal:
+ return _(TYPE_HANDLINGSIGNAL_STR);
+ break;
+ default:
+ return QString::number((int)typeEnum);
+ }
+}
+
+
+QmlProfilerFileReader::QmlProfilerFileReader(QObject *parent) :
+ QObject(parent),
+ m_v8Model(0)
+{
+}
+
+void QmlProfilerFileReader::setV8DataModel(QV8ProfilerDataModel *dataModel)
+{
+ m_v8Model = dataModel;
+}
+
+bool QmlProfilerFileReader::load(QIODevice *device)
+{
+ QXmlStreamReader stream(device);
+
+ bool validVersion = true;
+
+ 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")))
+ emit traceStartTime(attributes.value(_("traceStart")).toString().toLongLong());
+ if (attributes.hasAttribute(_("traceEnd")))
+ emit traceEndTime(attributes.value(_("traceEnd")).toString().toLongLong());
+ }
+
+ if (elementName == _("eventData")) {
+ loadEventData(stream);
+ break;
+ }
+
+ if (elementName == _("profilerDataModel")) {
+ loadProfilerDataModel(stream);
+ break;
+ }
+
+ if (elementName == _("v8profile")) {
+ if (m_v8Model)
+ m_v8Model->load(stream);
+ break;
+ }
+
+ break;
+ }
+ default: break;
+ }
+ }
+
+ if (stream.hasError()) {
+ emit error(tr("Error while parsing trace data file: %1").arg(stream.errorString()));
+ return false;
+ } else {
+ processQmlEvents();
+
+ return true;
+ }
+}
+
+void QmlProfilerFileReader::loadEventData(QXmlStreamReader &stream)
+{
+ QTC_ASSERT(stream.name().toString() == _("eventData"), return);
+
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute(_("totalTime"))) {
+ // not used any more
+ }
+
+ int eventIndex = -1;
+ QmlEvent event = {
+ QString(), // displayname
+ QString(), // filename
+ QString(), // details
+ Painting, // type
+ QmlBinding, // bindingType, set for backwards compatibility
+ 0, // line
+ 0 // column
+ };
+ const QmlEvent defaultEvent = event;
+
+ while (!stream.atEnd() && !stream.hasError()) {
+ QXmlStreamReader::TokenType token = stream.readNext();
+ const QString elementName = stream.name().toString();
+
+ switch (token) {
+ case QXmlStreamReader::StartElement: {
+ if (elementName == _("event")) {
+ event = defaultEvent;
+
+ const QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute(_("index"))) {
+ eventIndex = attributes.value(_("index")).toString().toInt();
+ } else {
+ // ignore event
+ eventIndex = -1;
+ }
+ break;
+ }
+
+ stream.readNext();
+ if (stream.tokenType() != QXmlStreamReader::Characters)
+ break;
+
+ const QString readData = stream.text().toString();
+
+ if (elementName == _("displayname")) {
+ event.displayName = readData;
+ break;
+ }
+
+ if (elementName == _("type")) {
+ event.type = qmlEventTypeAsEnum(readData);
+ break;
+ }
+
+ if (elementName == _("filename")) {
+ event.filename = readData;
+ break;
+ }
+
+ if (elementName == _("line")) {
+ event.line = readData.toInt();
+ break;
+ }
+
+ if (elementName == _("column")) {
+ event.column = readData.toInt();
+ break;
+ }
+
+ if (elementName == _("details")) {
+ event.details = readData;
+ break;
+ }
+
+ if (elementName == _("bindingType")) {
+ event.bindingType = readData.toInt();
+ break;
+ }
+
+ break;
+ }
+ case QXmlStreamReader::EndElement: {
+ if (elementName == _("event")) {
+ if (eventIndex >= 0) {
+ if (eventIndex >= m_qmlEvents.size())
+ m_qmlEvents.resize(eventIndex + 1);
+ m_qmlEvents[eventIndex] = event;
+ }
+ break;
+ }
+
+ if (elementName == _("eventData")) {
+ // done reading eventData
+ return;
+ }
+ break;
+ }
+ default: break;
+ } // switch
+ }
+}
+
+void QmlProfilerFileReader::loadProfilerDataModel(QXmlStreamReader &stream)
+{
+ QTC_ASSERT(stream.name().toString() == _("profilerDataModel"), return);
+
+ while (!stream.atEnd() && !stream.hasError()) {
+ QXmlStreamReader::TokenType token = stream.readNext();
+ const QString elementName = stream.name().toString();
+
+ switch (token) {
+ case QXmlStreamReader::StartElement: {
+ if (elementName == _("range")) {
+ Range range = { 0, 0 };
+
+ const QXmlStreamAttributes attributes = stream.attributes();
+ if (!attributes.hasAttribute(_("startTime"))
+ || !attributes.hasAttribute(_("duration"))
+ || !attributes.hasAttribute(_("eventIndex"))) {
+ // ignore incomplete entry
+ continue;
+ }
+
+ range.startTime = attributes.value(_("startTime")).toString().toLongLong();
+ range.duration = attributes.value(_("duration")).toString().toLongLong();
+ int eventIndex = attributes.value(_("eventIndex")).toString().toInt();
+
+ m_ranges.append(QPair<Range,int>(range, eventIndex));
+ }
+ break;
+ }
+ case QXmlStreamReader::EndElement: {
+ if (elementName == _("profilerDataModel")) {
+ // done reading profilerDataModel
+ return;
+ }
+ break;
+ }
+ default: break;
+ } // switch
+ }
+}
+
+void QmlProfilerFileReader::processQmlEvents()
+{
+ for (int i = 0; i < m_ranges.size(); ++i) {
+ Range range = m_ranges[i].first;
+ int eventIndex = m_ranges[i].second;
+
+ if (eventIndex < 0 || eventIndex >= m_qmlEvents.size()) {
+ qWarning() << ".qtd file - range index" << eventIndex
+ << "is outside of bounds (0, " << m_qmlEvents.size() << ")";
+ continue;
+ }
+
+ QmlEvent &event = m_qmlEvents[eventIndex];
+
+ emit rangedEvent(event.type, event.bindingType, range.startTime, range.duration,
+ QStringList(event.displayName), QmlEventLocation(event.filename,
+ event.line,
+ event.column));
+ }
+}
+
+QmlProfilerFileWriter::QmlProfilerFileWriter(QObject *parent) :
+ QObject(parent),
+ m_startTime(0),
+ m_endTime(0),
+ m_measuredTime(0),
+ m_v8Model(0)
+{
+ m_acceptedTypes << QmlDebug::Compiling << QmlDebug::Creating << QmlDebug::Binding << QmlDebug::HandlingSignal;
+}
+
+void QmlProfilerFileWriter::setTraceTime(qint64 startTime, qint64 endTime, qint64 measuredTime)
+{
+ m_startTime = startTime;
+ m_endTime = endTime;
+ m_measuredTime = measuredTime;
+}
+
+void QmlProfilerFileWriter::setV8DataModel(QV8ProfilerDataModel *dataModel)
+{
+ m_v8Model = dataModel;
+}
+
+void QmlProfilerFileWriter::setQmlEvents(const QVector<QmlProfilerSimpleModel::QmlEventData> &events)
+{
+ foreach (const QmlProfilerSimpleModel::QmlEventData &event, events) {
+ const QString hashStr = QmlProfilerSimpleModel::getHashString(event);
+ if (!m_qmlEvents.contains(hashStr)) {
+ QmlEvent e = { event.displayName,
+ event.location.filename,
+ event.data.join(_("")),
+ static_cast<QmlDebug::QmlEventType>(event.eventType),
+ event.bindingType,
+ event.location.line,
+ event.location.column
+ };
+ m_qmlEvents.insert(hashStr, e);
+ }
+
+ Range r = { event.startTime, event.duration, };
+ m_ranges.append(QPair<Range, QString>(r, hashStr));
+ }
+
+ calculateMeasuredTime(events);
+}
+
+void QmlProfilerFileWriter::save(QIODevice *device)
+{
+ QXmlStreamWriter stream(device);
+
+ stream.setAutoFormatting(true);
+ stream.writeStartDocument();
+
+ stream.writeStartElement(_("trace"));
+ stream.writeAttribute(_("version"), _(PROFILER_FILE_VERSION));
+
+ stream.writeAttribute(_("traceStart"), QString::number(m_startTime));
+ stream.writeAttribute(_("traceEnd"), QString::number(m_endTime));
+
+ stream.writeStartElement(_("eventData"));
+ stream.writeAttribute(_("totalTime"), QString::number(m_measuredTime));
+
+ QHash<QString,QmlEvent>::const_iterator eventIter = m_qmlEvents.constBegin();
+ for (; eventIter != m_qmlEvents.constEnd(); ++eventIter) {
+ QmlEvent event = eventIter.value();
+
+ stream.writeStartElement(_("event"));
+ stream.writeAttribute(_("index"), QString::number(m_qmlEvents.keys().indexOf(eventIter.key())));
+ stream.writeTextElement(_("displayname"), event.displayName);
+ stream.writeTextElement(_("type"), qmlEventTypeAsString(event.type));
+ if (!event.filename.isEmpty()) {
+ stream.writeTextElement(_("filename"), event.filename);
+ stream.writeTextElement(_("line"), QString::number(event.line));
+ stream.writeTextElement(_("column"), QString::number(event.column));
+ }
+ stream.writeTextElement(_("details"), event.details);
+ if (event.type == Binding)
+ stream.writeTextElement(_("bindingType"), QString::number((int)event.bindingType));
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // eventData
+
+ stream.writeStartElement(_("profilerDataModel"));
+
+ QVector<QPair<Range, QString> >::const_iterator rangeIter = m_ranges.constBegin();
+ for (; rangeIter != m_ranges.constEnd(); ++rangeIter) {
+ Range range = rangeIter->first;
+ QString eventHash = rangeIter->second;
+
+ stream.writeStartElement(_("range"));
+ stream.writeAttribute(_("startTime"), QString::number(range.startTime));
+ stream.writeAttribute(_("duration"), QString::number(range.duration));
+ stream.writeAttribute(_("eventIndex"), QString::number(m_qmlEvents.keys().indexOf(eventHash)));
+
+ QmlEvent event = m_qmlEvents.value(eventHash);
+// if (event.type == QmlDebug::Painting && range.animationCount >= 0) {
+// // animation frame
+// stream.writeAttribute(_("framerate"), QString::number(rangedEvent.frameRate));
+// stream.writeAttribute(_("animationcount"), QString::number(rangedEvent.animationCount));
+// }
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // profilerDataModel
+
+ m_v8Model->save(stream);
+
+ stream.writeEndElement(); // trace
+ stream.writeEndDocument();
+}
+
+void QmlProfilerFileWriter::calculateMeasuredTime(const QVector<QmlProfilerSimpleModel::QmlEventData> &events)
+{
+ // measured time isn't used, but old clients might still need it
+ // -> we calculate it explicitly
+
+ qint64 duration = 0;
+
+ QHash<int, qint64> endtimesPerLevel;
+ int level = QmlDebug::Constants::QML_MIN_LEVEL;
+ endtimesPerLevel[0] = 0;
+
+ foreach (const QmlProfilerSimpleModel::QmlEventData &event, events) {
+ // whitelist
+ if (!m_acceptedTypes.contains(event.eventType))
+ continue;
+
+ // level computation
+ if (endtimesPerLevel[level] > event.startTime) {
+ level++;
+ } else {
+ while (level > QmlDebug::Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= event.startTime)
+ level--;
+ }
+ endtimesPerLevel[level] = event.startTime + event.duration;
+ if (level == QmlDebug::Constants::QML_MIN_LEVEL) {
+ duration += event.duration;
+ }
+ }
+
+ m_measuredTime = duration;
+}
+
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/plugins/qmlprofiler/qmlprofilertracefile.h b/plugins/qmlprofiler/qmlprofilertracefile.h
new file mode 100644
index 0000000000..f5fd4dbb96
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilertracefile.h
@@ -0,0 +1,127 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERTRACEFILE_H
+#define QMLPROFILERTRACEFILE_H
+
+#include <QObject>
+#include <QVector>
+#include <QString>
+
+#include <qmldebug/qmlprofilereventlocation.h>
+#include <qmldebug/qmlprofilereventtypes.h>
+
+#include "qmlprofilersimplemodel.h"
+#include "qv8profilerdatamodel.h"
+
+QT_FORWARD_DECLARE_CLASS(QIODevice)
+QT_FORWARD_DECLARE_CLASS(QXmlStreamReader)
+
+
+namespace QmlProfiler {
+namespace Internal {
+
+
+struct QmlEvent {
+ QString displayName;
+ QString filename;
+ QString details;
+ QmlDebug::QmlEventType type;
+ int bindingType;
+ int line;
+ int column;
+};
+
+struct Range {
+ qint64 startTime;
+ qint64 duration;
+};
+
+class QmlProfilerFileReader : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit QmlProfilerFileReader(QObject *parent = 0);
+
+ void setV8DataModel(QV8ProfilerDataModel *dataModel);
+
+ bool load(QIODevice *device);
+
+signals:
+ void traceStartTime(qint64 traceStartTime);
+ void traceEndTime(qint64 traceStartTime);
+
+ void rangedEvent(int type, int bindingType, qint64 startTime, qint64 length,
+ const QStringList &data, const QmlDebug::QmlEventLocation &location);
+
+ void error(const QString &error);
+
+private:
+ void loadEventData(QXmlStreamReader &reader);
+ void loadProfilerDataModel(QXmlStreamReader &reader);
+
+ void processQmlEvents();
+
+
+ QV8ProfilerDataModel *m_v8Model;
+ QVector<QmlEvent> m_qmlEvents;
+ QVector<QPair<Range, int> > m_ranges;
+};
+
+
+class QmlProfilerFileWriter : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit QmlProfilerFileWriter(QObject *parent = 0);
+
+ void setTraceTime(qint64 startTime, qint64 endTime, qint64 measturedTime);
+ void setV8DataModel(QV8ProfilerDataModel *dataModel);
+ void setQmlEvents(const QVector<QmlProfilerSimpleModel::QmlEventData> &events);
+
+ void save(QIODevice *device);
+
+private:
+ void calculateMeasuredTime(const QVector<QmlProfilerSimpleModel::QmlEventData> &events);
+
+
+ qint64 m_startTime, m_endTime, m_measuredTime;
+ QV8ProfilerDataModel *m_v8Model;
+ QHash<QString,QmlEvent> m_qmlEvents;
+ QVector<QPair<Range, QString> > m_ranges;
+ QVector <int> m_acceptedTypes;
+};
+
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILERTRACEFILE_H
diff --git a/plugins/qmlprofiler/qmlprofilertraceview.cpp b/plugins/qmlprofiler/qmlprofilertraceview.cpp
new file mode 100644
index 0000000000..d1dc885a80
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilertraceview.cpp
@@ -0,0 +1,604 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilertraceview.h"
+#include "qmlprofilertool.h"
+#include "qmlprofilerstatemanager.h"
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilertimelinemodelproxy.h"
+#include "qmlprofileroverviewmodelproxy.h"
+
+// Needed for the load&save actions in the context menu
+#include <analyzerbase/ianalyzertool.h>
+
+// Communication with the other views (limit events to range)
+#include "qmlprofilerviewmanager.h"
+
+#include <utils/styledbar.h>
+
+#include <QDeclarativeContext>
+#include <QToolButton>
+#include <QEvent>
+#include <QVBoxLayout>
+#include <QGraphicsObject>
+#include <QScrollBar>
+#include <QSlider>
+#include <QMenu>
+
+#include <math.h>
+
+using namespace QmlDebug;
+
+namespace QmlProfiler {
+namespace Internal {
+
+const int sliderTicks = 10000;
+const qreal sliderExp = 3;
+
+
+/////////////////////////////////////////////////////////
+bool MouseWheelResizer::eventFilter(QObject *obj, QEvent *event)
+{
+ if (event->type() == QEvent::Wheel) {
+ QWheelEvent *ev = static_cast<QWheelEvent *>(event);
+ if (ev->modifiers() & Qt::ControlModifier) {
+ emit mouseWheelMoved(ev->pos().x(), ev->pos().y(), ev->delta());
+ return true;
+ }
+ }
+ return QObject::eventFilter(obj, event);
+}
+
+/////////////////////////////////////////////////////////
+void ZoomControl::setRange(qint64 startTime, qint64 endTime)
+{
+ if (m_startTime != startTime || m_endTime != endTime) {
+ m_startTime = startTime;
+ m_endTime = endTime;
+ emit rangeChanged();
+ }
+}
+
+/////////////////////////////////////////////////////////
+ScrollableDeclarativeView::ScrollableDeclarativeView(QWidget *parent)
+ : QDeclarativeView(parent)
+{
+}
+
+ScrollableDeclarativeView::~ScrollableDeclarativeView()
+{
+}
+
+void ScrollableDeclarativeView::scrollContentsBy(int dx, int dy)
+{
+ // special workaround to track the scrollbar
+ if (rootObject()) {
+ int scrollY = rootObject()->property("scrollY").toInt();
+ rootObject()->setProperty("scrollY", QVariant(scrollY - dy));
+ }
+ QDeclarativeView::scrollContentsBy(dx,dy);
+}
+
+/////////////////////////////////////////////////////////
+class QmlProfilerTraceView::QmlProfilerTraceViewPrivate
+{
+public:
+ QmlProfilerTraceViewPrivate(QmlProfilerTraceView *qq) : q(qq) {}
+ QmlProfilerTraceView *q;
+
+ QmlProfilerStateManager *m_profilerState;
+ Analyzer::IAnalyzerTool *m_profilerTool;
+ QmlProfilerViewManager *m_viewContainer;
+
+ QSize m_sizeHint;
+
+ ScrollableDeclarativeView *m_mainView;
+ QDeclarativeView *m_timebar;
+ QDeclarativeView *m_overview;
+ QmlProfilerModelManager *m_modelManager;
+ QmlProfilerTimelineModelProxy *m_modelProxy;
+ QmlProfilerOverviewModelProxy *m_overviewProxy;
+
+ ZoomControl *m_zoomControl;
+
+ QToolButton *m_buttonRange;
+ QToolButton *m_buttonLock;
+ QWidget *m_zoomToolbar;
+ int m_currentZoomLevel;
+};
+
+QmlProfilerTraceView::QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerTool *profilerTool, QmlProfilerViewManager *container, QmlProfilerModelManager *modelManager, QmlProfilerStateManager *profilerState)
+ : QWidget(parent), d(new QmlProfilerTraceViewPrivate(this))
+{
+ setObjectName(QLatin1String("QML Profiler"));
+
+ d->m_zoomControl = new ZoomControl(this);
+ connect(d->m_zoomControl, SIGNAL(rangeChanged()), this, SLOT(updateRange()));
+
+ QVBoxLayout *groupLayout = new QVBoxLayout;
+ groupLayout->setContentsMargins(0, 0, 0, 0);
+ groupLayout->setSpacing(0);
+
+ d->m_mainView = new ScrollableDeclarativeView(this);
+ d->m_mainView->setResizeMode(QDeclarativeView::SizeViewToRootObject);
+ d->m_mainView->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ d->m_mainView->setBackgroundBrush(QBrush(Qt::white));
+ d->m_mainView->setAlignment(Qt::AlignLeft | Qt::AlignTop);
+ d->m_mainView->setFocus();
+
+ MouseWheelResizer *resizer = new MouseWheelResizer(this);
+ connect(resizer,SIGNAL(mouseWheelMoved(int,int,int)), this, SLOT(mouseWheelMoved(int,int,int)));
+ d->m_mainView->viewport()->installEventFilter(resizer);
+
+ QHBoxLayout *toolsLayout = new QHBoxLayout;
+
+ d->m_timebar = new QDeclarativeView(this);
+ d->m_timebar->setResizeMode(QDeclarativeView::SizeRootObjectToView);
+ d->m_timebar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ d->m_timebar->setFixedHeight(24);
+
+ d->m_overview = new QDeclarativeView(this);
+ d->m_overview->setResizeMode(QDeclarativeView::SizeRootObjectToView);
+ d->m_overview->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ d->m_overview->setMaximumHeight(50);
+
+ d->m_zoomToolbar = createZoomToolbar();
+ d->m_zoomToolbar->move(0, d->m_timebar->height());
+ d->m_zoomToolbar->setVisible(false);
+
+ toolsLayout->addWidget(createToolbar());
+ toolsLayout->addWidget(d->m_timebar);
+ emit enableToolbar(false);
+
+ groupLayout->addLayout(toolsLayout);
+ groupLayout->addWidget(d->m_mainView);
+ groupLayout->addWidget(d->m_overview);
+
+ setLayout(groupLayout);
+
+ d->m_profilerTool = profilerTool;
+ d->m_viewContainer = container;
+ d->m_modelManager = modelManager;
+ d->m_modelProxy = new QmlProfilerTimelineModelProxy(modelManager, this);
+ d->m_overviewProxy = new QmlProfilerOverviewModelProxy(modelManager, this);
+ connect(d->m_modelManager, SIGNAL(stateChanged()),
+ this, SLOT(profilerDataModelStateChanged()));
+ d->m_mainView->rootContext()->setContextProperty(QLatin1String("qmlProfilerModelProxy"),
+ d->m_modelProxy);
+ d->m_overview->rootContext()->setContextProperty(QLatin1String("qmlProfilerModelProxy"),
+ d->m_overviewProxy);
+
+ d->m_profilerState = profilerState;
+ connect(d->m_profilerState, SIGNAL(stateChanged()),
+ this, SLOT(profilerStateChanged()));
+ connect(d->m_profilerState, SIGNAL(clientRecordingChanged()),
+ this, SLOT(clientRecordingChanged()));
+ connect(d->m_profilerState, SIGNAL(serverRecordingChanged()),
+ this, SLOT(serverRecordingChanged()));
+
+ // Minimum height: 5 rows of 20 pixels + scrollbar of 50 pixels + 20 pixels margin
+ setMinimumHeight(170);
+ d->m_currentZoomLevel = 0;
+}
+
+QmlProfilerTraceView::~QmlProfilerTraceView()
+{
+ delete d;
+}
+
+/////////////////////////////////////////////////////////
+// Initialize widgets
+void QmlProfilerTraceView::reset()
+{
+ d->m_mainView->rootContext()->setContextProperty(QLatin1String("zoomControl"), d->m_zoomControl);
+ d->m_timebar->rootContext()->setContextProperty(QLatin1String("zoomControl"), d->m_zoomControl);
+ d->m_overview->rootContext()->setContextProperty(QLatin1String("zoomControl"), d->m_zoomControl);
+
+ d->m_timebar->setSource(QUrl(QLatin1String("qrc:/qmlprofiler/TimeDisplay.qml")));
+ d->m_overview->setSource(QUrl(QLatin1String("qrc:/qmlprofiler/Overview.qml")));
+
+ d->m_mainView->setSource(QUrl(QLatin1String("qrc:/qmlprofiler/MainView.qml")));
+ QGraphicsObject *rootObject = d->m_mainView->rootObject();
+ rootObject->setProperty("width", QVariant(width()));
+ rootObject->setProperty("candidateHeight", QVariant(height() - d->m_timebar->height() - d->m_overview->height()));
+
+ connect(rootObject, SIGNAL(updateCursorPosition()), this, SLOT(updateCursorPosition()));
+ connect(rootObject, SIGNAL(updateRangeButton()), this, SLOT(updateRangeButton()));
+ connect(rootObject, SIGNAL(updateLockButton()), this, SLOT(updateLockButton()));
+ connect(this, SIGNAL(jumpToPrev()), rootObject, SLOT(prevEvent()));
+ connect(this, SIGNAL(jumpToNext()), rootObject, SLOT(nextEvent()));
+ connect(rootObject, SIGNAL(selectedEventChanged(int)), this, SIGNAL(selectedEventChanged(int)));
+ connect(rootObject, SIGNAL(changeToolTip(QString)), this, SLOT(updateToolTip(QString)));
+ connect(rootObject, SIGNAL(updateVerticalScroll(int)), this, SLOT(updateVerticalScroll(int)));
+}
+
+QWidget *QmlProfilerTraceView::createToolbar()
+{
+ Utils::StyledBar *bar = new Utils::StyledBar(this);
+ bar->setStyleSheet(QLatin1String("background: #9B9B9B"));
+ bar->setSingleRow(true);
+ bar->setFixedWidth(150);
+ bar->setFixedHeight(24);
+
+ QHBoxLayout *toolBarLayout = new QHBoxLayout(bar);
+ toolBarLayout->setMargin(0);
+ toolBarLayout->setSpacing(0);
+
+ QToolButton *buttonPrev= new QToolButton;
+ buttonPrev->setIcon(QIcon(QLatin1String(":/qmlprofiler/ico_prev.png")));
+ buttonPrev->setToolTip(tr("Jump to previous event"));
+ connect(buttonPrev, SIGNAL(clicked()), this, SIGNAL(jumpToPrev()));
+ connect(this, SIGNAL(enableToolbar(bool)), buttonPrev, SLOT(setEnabled(bool)));
+
+ QToolButton *buttonNext= new QToolButton;
+ buttonNext->setIcon(QIcon(QLatin1String(":/qmlprofiler/ico_next.png")));
+ buttonNext->setToolTip(tr("Jump to next event"));
+ connect(buttonNext, SIGNAL(clicked()), this, SIGNAL(jumpToNext()));
+ connect(this, SIGNAL(enableToolbar(bool)), buttonNext, SLOT(setEnabled(bool)));
+
+ QToolButton *buttonZoomControls = new QToolButton;
+ buttonZoomControls->setIcon(QIcon(QLatin1String(":/qmlprofiler/ico_zoom.png")));
+ buttonZoomControls->setToolTip(tr("Show zoom slider"));
+ buttonZoomControls->setCheckable(true);
+ buttonZoomControls->setChecked(false);
+ connect(buttonZoomControls, SIGNAL(toggled(bool)), d->m_zoomToolbar, SLOT(setVisible(bool)));
+ connect(this, SIGNAL(enableToolbar(bool)), buttonZoomControls, SLOT(setEnabled(bool)));
+
+ d->m_buttonRange = new QToolButton;
+ d->m_buttonRange->setIcon(QIcon(QLatin1String(":/qmlprofiler/ico_rangeselection.png")));
+ d->m_buttonRange->setToolTip(tr("Select range"));
+ d->m_buttonRange->setCheckable(true);
+ d->m_buttonRange->setChecked(false);
+ connect(d->m_buttonRange, SIGNAL(clicked(bool)), this, SLOT(toggleRangeMode(bool)));
+ connect(this, SIGNAL(enableToolbar(bool)), d->m_buttonRange, SLOT(setEnabled(bool)));
+ connect(this, SIGNAL(rangeModeChanged(bool)), d->m_buttonRange, SLOT(setChecked(bool)));
+
+ d->m_buttonLock = new QToolButton;
+ d->m_buttonLock->setIcon(QIcon(QLatin1String(":/qmlprofiler/ico_selectionmode.png")));
+ d->m_buttonLock->setToolTip(tr("View event information on mouseover"));
+ d->m_buttonLock->setCheckable(true);
+ d->m_buttonLock->setChecked(false);
+ connect(d->m_buttonLock, SIGNAL(clicked(bool)), this, SLOT(toggleLockMode(bool)));
+ connect(this, SIGNAL(enableToolbar(bool)), d->m_buttonLock, SLOT(setEnabled(bool)));
+ connect(this, SIGNAL(lockModeChanged(bool)), d->m_buttonLock, SLOT(setChecked(bool)));
+
+ toolBarLayout->addWidget(buttonPrev);
+ toolBarLayout->addWidget(buttonNext);
+ toolBarLayout->addWidget(new Utils::StyledSeparator());
+ toolBarLayout->addWidget(buttonZoomControls);
+ toolBarLayout->addWidget(new Utils::StyledSeparator());
+ toolBarLayout->addWidget(d->m_buttonRange);
+ toolBarLayout->addWidget(d->m_buttonLock);
+
+ return bar;
+}
+
+
+QWidget *QmlProfilerTraceView::createZoomToolbar()
+{
+ Utils::StyledBar *bar = new Utils::StyledBar(this);
+ bar->setStyleSheet(QLatin1String("background: #9B9B9B"));
+ bar->setSingleRow(true);
+ bar->setFixedWidth(150);
+ bar->setFixedHeight(24);
+
+ QHBoxLayout *toolBarLayout = new QHBoxLayout(bar);
+ toolBarLayout->setMargin(0);
+ toolBarLayout->setSpacing(0);
+
+ QSlider *zoomSlider = new QSlider(Qt::Horizontal);
+ zoomSlider->setFocusPolicy(Qt::NoFocus);
+ zoomSlider->setRange(1, sliderTicks);
+ zoomSlider->setInvertedAppearance(true);
+ zoomSlider->setPageStep(sliderTicks/100);
+ connect(this, SIGNAL(enableToolbar(bool)), zoomSlider, SLOT(setEnabled(bool)));
+ connect(zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(setZoomLevel(int)));
+ connect(this, SIGNAL(zoomLevelChanged(int)), zoomSlider, SLOT(setValue(int)));
+ zoomSlider->setStyleSheet(QLatin1String("\
+ QSlider:horizontal {\
+ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #444444, stop: 1 #5a5a5a);\
+ border: 1px #313131;\
+ height: 20px;\
+ margin: 0px 0px 0px 0px;\
+ }\
+ QSlider::add-page:horizontal {\
+ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #5a5a5a, stop: 1 #444444);\
+ border: 1px #313131;\
+ }\
+ QSlider::sub-page:horizontal {\
+ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #5a5a5a, stop: 1 #444444);\
+ border: 1px #313131;\
+ }\
+ "));
+
+ toolBarLayout->addWidget(zoomSlider);
+
+ return bar;
+}
+
+/////////////////////////////////////////////////////////
+bool QmlProfilerTraceView::hasValidSelection() const
+{
+ QGraphicsObject *rootObject = d->m_mainView->rootObject();
+ if (rootObject)
+ return rootObject->property("selectionRangeReady").toBool();
+ return false;
+}
+
+qint64 QmlProfilerTraceView::selectionStart() const
+{
+ QGraphicsObject *rootObject = d->m_mainView->rootObject();
+ if (rootObject)
+ return rootObject->property("selectionRangeStart").toLongLong();
+ return 0;
+}
+
+qint64 QmlProfilerTraceView::selectionEnd() const
+{
+ QGraphicsObject *rootObject = d->m_mainView->rootObject();
+ if (rootObject)
+ return rootObject->property("selectionRangeEnd").toLongLong();
+ return 0;
+}
+
+void QmlProfilerTraceView::clearDisplay()
+{
+ d->m_zoomControl->setRange(0,0);
+
+ updateVerticalScroll(0);
+ d->m_mainView->rootObject()->setProperty("scrollY", QVariant(0));
+
+ QMetaObject::invokeMethod(d->m_mainView->rootObject(), "clearAll");
+ QMetaObject::invokeMethod(d->m_overview->rootObject(), "clearDisplay");
+}
+
+void QmlProfilerTraceView::selectNextEventWithId(int eventId)
+{
+ QGraphicsObject *rootObject = d->m_mainView->rootObject();
+ if (rootObject)
+ QMetaObject::invokeMethod(rootObject, "selectNextWithId",
+ Q_ARG(QVariant,QVariant(eventId)));
+}
+
+/////////////////////////////////////////////////////////
+// Goto source location
+void QmlProfilerTraceView::updateCursorPosition()
+{
+ QGraphicsObject *rootObject = d->m_mainView->rootObject();
+ emit gotoSourceLocation(rootObject->property("fileName").toString(),
+ rootObject->property("lineNumber").toInt(),
+ rootObject->property("columnNumber").toInt());
+}
+
+/////////////////////////////////////////////////////////
+// Toolbar buttons
+void QmlProfilerTraceView::toggleRangeMode(bool active)
+{
+ QGraphicsObject *rootObject = d->m_mainView->rootObject();
+ bool rangeMode = rootObject->property("selectionRangeMode").toBool();
+ if (active != rangeMode) {
+ if (active)
+ d->m_buttonRange->setIcon(QIcon(QLatin1String(":/qmlprofiler/ico_rangeselected.png")));
+ else
+ d->m_buttonRange->setIcon(QIcon(QLatin1String(":/qmlprofiler/ico_rangeselection.png")));
+ rootObject->setProperty("selectionRangeMode", QVariant(active));
+ }
+}
+
+void QmlProfilerTraceView::updateRangeButton()
+{
+ bool rangeMode = d->m_mainView->rootObject()->property("selectionRangeMode").toBool();
+ if (rangeMode)
+ d->m_buttonRange->setIcon(QIcon(QLatin1String(":/qmlprofiler/ico_rangeselected.png")));
+ else
+ d->m_buttonRange->setIcon(QIcon(QLatin1String(":/qmlprofiler/ico_rangeselection.png")));
+ emit rangeModeChanged(rangeMode);
+}
+
+void QmlProfilerTraceView::toggleLockMode(bool active)
+{
+ QGraphicsObject *rootObject = d->m_mainView->rootObject();
+ bool lockMode = !rootObject->property("selectionLocked").toBool();
+ if (active != lockMode) {
+ rootObject->setProperty("selectionLocked", QVariant(!active));
+ rootObject->setProperty("selectedItem", QVariant(-1));
+ }
+}
+
+void QmlProfilerTraceView::updateLockButton()
+{
+ bool lockMode = !d->m_mainView->rootObject()->property("selectionLocked").toBool();
+ emit lockModeChanged(lockMode);
+}
+
+////////////////////////////////////////////////////////
+// Zoom control
+void QmlProfilerTraceView::setZoomLevel(int zoomLevel)
+{
+ if (d->m_currentZoomLevel != zoomLevel && d->m_mainView->rootObject()) {
+ QVariant newFactor = pow(qreal(zoomLevel) / qreal(sliderTicks), sliderExp);
+ d->m_currentZoomLevel = zoomLevel;
+ QMetaObject::invokeMethod(d->m_mainView->rootObject(), "updateWindowLength", Q_ARG(QVariant, newFactor));
+ }
+}
+
+void QmlProfilerTraceView::updateRange()
+{
+ if (!d->m_modelManager)
+ return;
+ qreal duration = d->m_zoomControl->endTime() - d->m_zoomControl->startTime();
+ if (duration <= 0)
+ return;
+ if (d->m_modelManager->traceTime()->duration() <= 0)
+ return;
+ int newLevel = pow(duration / d->m_modelManager->traceTime()->duration(), 1/sliderExp) * sliderTicks;
+ if (d->m_currentZoomLevel != newLevel) {
+ d->m_currentZoomLevel = newLevel;
+ emit zoomLevelChanged(newLevel);
+ }
+}
+
+void QmlProfilerTraceView::mouseWheelMoved(int mouseX, int mouseY, int wheelDelta)
+{
+ Q_UNUSED(mouseY);
+ QGraphicsObject *rootObject = d->m_mainView->rootObject();
+ if (rootObject) {
+ QMetaObject::invokeMethod(rootObject, "wheelZoom",
+ Q_ARG(QVariant, QVariant(mouseX)),
+ Q_ARG(QVariant, QVariant(wheelDelta)));
+ }
+}
+////////////////////////////////////////////////////////
+void QmlProfilerTraceView::updateToolTip(const QString &text)
+{
+ setToolTip(text);
+}
+
+void QmlProfilerTraceView::updateVerticalScroll(int newPosition)
+{
+ d->m_mainView->verticalScrollBar()->setValue(newPosition);
+}
+
+void QmlProfilerTraceView::resizeEvent(QResizeEvent *event)
+{
+ QWidget::resizeEvent(event);
+ QGraphicsObject *rootObject = d->m_mainView->rootObject();
+ if (rootObject) {
+ rootObject->setProperty("width", QVariant(event->size().width()));
+ int newHeight = event->size().height() - d->m_timebar->height() - d->m_overview->height();
+ rootObject->setProperty("candidateHeight", QVariant(newHeight));
+ }
+ emit resized();
+}
+
+////////////////////////////////////////////////////////////////
+// Context menu
+void QmlProfilerTraceView::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QMenu menu;
+ QAction *viewAllAction = 0;
+
+ QmlProfilerTool *profilerTool = qobject_cast<QmlProfilerTool *>(d->m_profilerTool);
+
+ if (profilerTool)
+ menu.addActions(profilerTool->profilerContextMenuActions());
+
+ menu.addSeparator();
+
+ QAction *getLocalStatsAction = menu.addAction(tr("Limit Events Pane to Current Range"));
+ if (!d->m_viewContainer->hasValidSelection())
+ getLocalStatsAction->setEnabled(false);
+
+ QAction *getGlobalStatsAction = menu.addAction(tr("Reset Events Pane"));
+ if (d->m_viewContainer->hasGlobalStats())
+ getGlobalStatsAction->setEnabled(false);
+
+ if (d->m_modelProxy->count() > 0) {
+ menu.addSeparator();
+ viewAllAction = menu.addAction(tr("Reset Zoom"));
+ }
+
+ QAction *selectedAction = menu.exec(ev->globalPos());
+
+ if (selectedAction) {
+ if (selectedAction == viewAllAction) {
+ d->m_zoomControl->setRange(
+ d->m_modelManager->traceTime()->startTime(),
+ d->m_modelManager->traceTime()->endTime());
+ }
+ if (selectedAction == getLocalStatsAction) {
+ d->m_viewContainer->getStatisticsInRange(
+ d->m_viewContainer->selectionStart(),
+ d->m_viewContainer->selectionEnd());
+ }
+ if (selectedAction == getGlobalStatsAction) {
+ d->m_viewContainer->getStatisticsInRange(-1, -1);
+ }
+ }
+}
+
+/////////////////////////////////////////////////
+// Tell QML the state of the profiler
+void QmlProfilerTraceView::setRecording(bool recording)
+{
+ QGraphicsObject *rootObject = d->m_mainView->rootObject();
+ if (rootObject)
+ rootObject->setProperty("recordingEnabled", QVariant(recording));
+}
+
+void QmlProfilerTraceView::setAppKilled()
+{
+ QGraphicsObject *rootObject = d->m_mainView->rootObject();
+ if (rootObject)
+ rootObject->setProperty("appKilled",QVariant(true));
+}
+////////////////////////////////////////////////////////////////
+// Profiler State
+void QmlProfilerTraceView::profilerDataModelStateChanged()
+{
+ switch (d->m_modelManager->state()) {
+ case QmlProfilerDataState::Empty:
+ emit enableToolbar(false);
+ break;
+ case QmlProfilerDataState::AcquiringData: break;
+ case QmlProfilerDataState::ProcessingData: break;
+ case QmlProfilerDataState::Done:
+ emit enableToolbar(true);
+ break;
+ default:
+ break;
+ }
+}
+
+void QmlProfilerTraceView::profilerStateChanged()
+{
+ switch (d->m_profilerState->currentState()) {
+ case QmlProfilerStateManager::AppKilled : {
+ if (d->m_modelManager->state() == QmlProfilerDataState::AcquiringData)
+ setAppKilled();
+ break;
+ }
+ default:
+ // no special action needed for other states
+ break;
+ }
+}
+
+void QmlProfilerTraceView::clientRecordingChanged()
+{
+ // nothing yet
+}
+
+void QmlProfilerTraceView::serverRecordingChanged()
+{
+ setRecording(d->m_profilerState->serverRecording());
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/plugins/qmlprofiler/qmlprofilertraceview.h b/plugins/qmlprofiler/qmlprofilertraceview.h
new file mode 100644
index 0000000000..f33e80fb90
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilertraceview.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERTRACEVIEW_H
+#define QMLPROFILERTRACEVIEW_H
+
+#include <QDeclarativeView>
+
+namespace Analyzer {
+class IAnalyzerTool;
+}
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerStateManager;
+class QmlProfilerViewManager;
+
+// capture mouse wheel events
+class MouseWheelResizer : public QObject {
+ Q_OBJECT
+public:
+ MouseWheelResizer(QObject *parent=0):QObject(parent){}
+protected:
+ bool eventFilter(QObject *obj, QEvent *event);
+signals:
+ void mouseWheelMoved(int x, int y, int delta);
+};
+
+// centralized zoom control
+class ZoomControl : public QObject {
+ Q_OBJECT
+public:
+ ZoomControl(QObject *parent=0):QObject(parent),m_startTime(0),m_endTime(0) {}
+ ~ZoomControl(){}
+
+ Q_INVOKABLE void setRange(qint64 startTime, qint64 endTime);
+ Q_INVOKABLE qint64 startTime() { return m_startTime; }
+ Q_INVOKABLE qint64 endTime() { return m_endTime; }
+
+signals:
+ void rangeChanged();
+
+private:
+ qint64 m_startTime;
+ qint64 m_endTime;
+};
+
+class ScrollableDeclarativeView : public QDeclarativeView
+{
+ Q_OBJECT
+public:
+ explicit ScrollableDeclarativeView(QWidget *parent = 0);
+ ~ScrollableDeclarativeView();
+protected:
+ void scrollContentsBy(int dx, int dy);
+};
+
+class QmlProfilerModelManager;
+class QmlProfilerTraceView : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerTool *profilerTool, QmlProfilerViewManager *container, QmlProfilerModelManager *modelManager, QmlProfilerStateManager *profilerState);
+ ~QmlProfilerTraceView();
+
+ void reset();
+
+ bool hasValidSelection() const;
+ qint64 selectionStart() const;
+ qint64 selectionEnd() const;
+
+public slots:
+ void clearDisplay();
+ void selectNextEventWithId(int eventId);
+
+private slots:
+ void updateCursorPosition();
+ void toggleRangeMode(bool);
+ void updateRangeButton();
+ void toggleLockMode(bool);
+ void updateLockButton();
+
+ void setZoomLevel(int zoomLevel);
+ void updateRange();
+ void mouseWheelMoved(int mouseX, int mouseY, int wheelDelta);
+
+ void updateToolTip(const QString &text);
+ void updateVerticalScroll(int newPosition);
+ void profilerDataModelStateChanged();
+
+protected:
+ virtual void resizeEvent(QResizeEvent *event);
+
+private slots:
+ void profilerStateChanged();
+ void clientRecordingChanged();
+ void serverRecordingChanged();
+
+signals:
+ void gotoSourceLocation(const QString &fileUrl, int lineNumber, int columNumber);
+ void selectedEventChanged(int eventId);
+
+ void jumpToPrev();
+ void jumpToNext();
+ void rangeModeChanged(bool);
+ void lockModeChanged(bool);
+ void enableToolbar(bool);
+ void zoomLevelChanged(int);
+
+ void resized();
+
+private:
+ void contextMenuEvent(QContextMenuEvent *);
+ QWidget *createToolbar();
+ QWidget *createZoomToolbar();
+
+ void setRecording(bool recording);
+ void setAppKilled();
+
+private:
+ class QmlProfilerTraceViewPrivate;
+ QmlProfilerTraceViewPrivate *d;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILERTRACEVIEW_H
+
diff --git a/plugins/qmlprofiler/qmlprofilertreeview.cpp b/plugins/qmlprofiler/qmlprofilertreeview.cpp
new file mode 100644
index 0000000000..2409e4383d
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilertreeview.cpp
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilertreeview.h"
+#include <QHeaderView>
+
+namespace QmlProfiler {
+namespace Internal {
+
+QmlProfilerTreeView::QmlProfilerTreeView(QWidget *parent)
+ : QTreeView(parent)
+{
+ setFrameStyle(QFrame::NoFrame);
+ header()->setResizeMode(QHeaderView::Interactive);
+ header()->setDefaultSectionSize(100);
+ header()->setMinimumSectionSize(50);
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/plugins/qmlprofiler/qmlprofilertreeview.h b/plugins/qmlprofiler/qmlprofilertreeview.h
new file mode 100644
index 0000000000..d0c5995963
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilertreeview.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERTREEVIEW
+#define QMLPROFILERTREEVIEW
+
+#include <QTreeView>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerTreeView : public QTreeView
+{
+ Q_OBJECT
+
+protected:
+ QmlProfilerTreeView(QWidget *parent = 0);
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILERTREEVIEW
diff --git a/plugins/qmlprofiler/qmlprofilerv8eventsview.cpp b/plugins/qmlprofiler/qmlprofilerv8eventsview.cpp
new file mode 100644
index 0000000000..89bd948cfb
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerv8eventsview.cpp
@@ -0,0 +1,1016 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilereventview.h"
+
+#include <QUrl>
+#include <QHash>
+
+#include <QStandardItem>
+#include <QHeaderView>
+
+#include <QApplication>
+#include <QClipboard>
+
+#include <QContextMenuEvent>
+#include <QDebug>
+
+#include <coreplugin/minisplitter.h>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+
+#include "qmlprofilerviewmanager.h"
+#include "qmlprofilertool.h"
+#include <QMenu>
+
+#include <utils/qtcassert.h>
+
+using namespace QmlDebug;
+
+namespace QmlProfiler {
+namespace Internal {
+
+#ifdef SHOW_BINDINGLOOPS
+struct Colors {
+ Colors () {
+ this->bindingLoopBackground = QColor("orange").lighter();
+ }
+
+ QColor bindingLoopBackground;
+};
+
+Q_GLOBAL_STATIC(Colors, colors)
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class EventsViewItem : public QStandardItem
+{
+public:
+ EventsViewItem(const QString &text) : QStandardItem(text) {}
+
+ virtual bool operator<(const QStandardItem &other) const
+ {
+ if (data().type() == QVariant::String) {
+ // first column
+ if (column() == 0) {
+ return data(FilenameRole).toString() == other.data(FilenameRole).toString() ?
+ data(LineRole).toInt() < other.data(LineRole).toInt() :
+ data(FilenameRole).toString() < other.data(FilenameRole).toString();
+ } else {
+ return data().toString().toLower() < other.data().toString().toLower();
+ }
+ }
+
+ return data().toDouble() < other.data().toDouble();
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class QmlProfilerEventsWidget::QmlProfilerEventsWidgetPrivate
+{
+public:
+ QmlProfilerEventsWidgetPrivate(QmlProfilerEventsWidget *qq):q(qq) {}
+ ~QmlProfilerEventsWidgetPrivate() {}
+
+ QmlProfilerEventsWidget *q;
+
+ Analyzer::IAnalyzerTool *m_profilerTool;
+ QmlProfilerViewManager *m_viewContainer;
+
+ QmlProfilerEventsMainView *m_eventTree;
+ QmlProfilerEventRelativesView *m_eventChildren;
+ QmlProfilerEventRelativesView *m_eventParents;
+
+ QmlProfilerEventsModelProxy *modelProxy;
+
+ bool m_globalStatsEnabled;
+};
+
+QmlProfilerEventsWidget::QmlProfilerEventsWidget(QWidget *parent,
+ Analyzer::IAnalyzerTool *profilerTool,
+ QmlProfilerViewManager *container,
+ QmlProfilerModelManager *profilerModelManager )
+ : QWidget(parent), d(new QmlProfilerEventsWidgetPrivate(this))
+{
+ setObjectName(QLatin1String("QmlProfilerEventsView"));
+
+ d->modelProxy = new QmlProfilerEventsModelProxy(profilerModelManager, this);
+ connect(profilerModelManager, SIGNAL(stateChanged()),
+ this, SLOT(profilerDataModelStateChanged()));
+
+ d->m_eventTree = new QmlProfilerEventsMainView(this, d->modelProxy);
+ connect(d->m_eventTree, SIGNAL(gotoSourceLocation(QString,int,int)), this, SIGNAL(gotoSourceLocation(QString,int,int)));
+ connect(d->m_eventTree, SIGNAL(showEventInTimeline(int)), this, SIGNAL(showEventInTimeline(int)));
+
+ d->m_eventChildren = new QmlProfilerEventRelativesView(
+ profilerModelManager,
+ new QmlProfilerEventChildrenModelProxy(profilerModelManager, this),
+ this);
+ d->m_eventParents = new QmlProfilerEventRelativesView(
+ profilerModelManager,
+ new QmlProfilerEventParentsModelProxy(profilerModelManager, this),
+ this);
+ connect(d->m_eventTree, SIGNAL(eventSelected(QString)), d->m_eventChildren, SLOT(displayEvent(QString)));
+ connect(d->m_eventTree, SIGNAL(eventSelected(QString)), d->m_eventParents, SLOT(displayEvent(QString)));
+ connect(d->m_eventChildren, SIGNAL(eventClicked(QString)), d->m_eventTree, SLOT(selectEvent(QString)));
+ connect(d->m_eventParents, SIGNAL(eventClicked(QString)), d->m_eventTree, SLOT(selectEvent(QString)));
+
+ // widget arrangement
+ QVBoxLayout *groupLayout = new QVBoxLayout;
+ groupLayout->setContentsMargins(0,0,0,0);
+ groupLayout->setSpacing(0);
+
+ Core::MiniSplitter *splitterVertical = new Core::MiniSplitter;
+ splitterVertical->addWidget(d->m_eventTree);
+ Core::MiniSplitter *splitterHorizontal = new Core::MiniSplitter;
+ splitterHorizontal->addWidget(d->m_eventParents);
+ splitterHorizontal->addWidget(d->m_eventChildren);
+ splitterHorizontal->setOrientation(Qt::Horizontal);
+ splitterVertical->addWidget(splitterHorizontal);
+ splitterVertical->setOrientation(Qt::Vertical);
+ splitterVertical->setStretchFactor(0,5);
+ splitterVertical->setStretchFactor(1,2);
+ groupLayout->addWidget(splitterVertical);
+ setLayout(groupLayout);
+
+ d->m_profilerTool = profilerTool;
+ d->m_viewContainer = container;
+ d->m_globalStatsEnabled = true;
+
+}
+
+QmlProfilerEventsWidget::~QmlProfilerEventsWidget()
+{
+ delete d->modelProxy;
+ delete d;
+}
+
+void QmlProfilerEventsWidget::profilerDataModelStateChanged()
+{
+}
+
+void QmlProfilerEventsWidget::clear()
+{
+ d->m_eventTree->clear();
+ d->m_eventChildren->clear();
+ d->m_eventParents->clear();
+}
+
+void QmlProfilerEventsWidget::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
+{
+ clear();
+ d->m_eventTree->getStatisticsInRange(rangeStart, rangeEnd);
+ d->m_globalStatsEnabled = d->m_eventTree->isRangeGlobal(rangeStart, rangeEnd);
+}
+
+QModelIndex QmlProfilerEventsWidget::selectedItem() const
+{
+ return d->m_eventTree->selectedItem();
+}
+
+void QmlProfilerEventsWidget::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QTC_ASSERT(d->m_viewContainer, return;);
+
+ QMenu menu;
+ QAction *copyRowAction = 0;
+ QAction *copyTableAction = 0;
+ QAction *showExtendedStatsAction = 0;
+ QAction *getLocalStatsAction = 0;
+ QAction *getGlobalStatsAction = 0;
+
+ QmlProfilerTool *profilerTool = qobject_cast<QmlProfilerTool *>(d->m_profilerTool);
+ QPoint position = ev->globalPos();
+
+ if (profilerTool) {
+ QList <QAction *> commonActions = profilerTool->profilerContextMenuActions();
+ foreach (QAction *act, commonActions) {
+ menu.addAction(act);
+ }
+ }
+
+ if (mouseOnTable(position)) {
+ menu.addSeparator();
+ if (selectedItem().isValid())
+ copyRowAction = menu.addAction(tr("Copy Row"));
+ copyTableAction = menu.addAction(tr("Copy Table"));
+
+ showExtendedStatsAction = menu.addAction(tr("Extended Event Statistics"));
+ showExtendedStatsAction->setCheckable(true);
+ showExtendedStatsAction->setChecked(showExtendedStatistics());
+ }
+
+ menu.addSeparator();
+ getLocalStatsAction = menu.addAction(tr("Limit Events Pane to Current Range"));
+ if (!d->m_viewContainer->hasValidSelection())
+ getLocalStatsAction->setEnabled(false);
+ getGlobalStatsAction = menu.addAction(tr("Reset Events Pane"));
+ if (hasGlobalStats())
+ getGlobalStatsAction->setEnabled(false);
+
+ QAction *selectedAction = menu.exec(position);
+
+ if (selectedAction) {
+ if (selectedAction == copyRowAction)
+ copyRowToClipboard();
+ if (selectedAction == copyTableAction)
+ copyTableToClipboard();
+ if (selectedAction == getLocalStatsAction) {
+ getStatisticsInRange(d->m_viewContainer->selectionStart(),
+ d->m_viewContainer->selectionEnd());
+ }
+
+ if (selectedAction == showExtendedStatsAction)
+ setShowExtendedStatistics(!showExtendedStatistics());
+ }
+}
+
+void QmlProfilerEventsWidget::resizeEvent(QResizeEvent *event)
+{
+ QWidget::resizeEvent(event);
+ emit resized();
+}
+
+bool QmlProfilerEventsWidget::mouseOnTable(const QPoint &position) const
+{
+ QPoint tableTopLeft = d->m_eventTree->mapToGlobal(QPoint(0,0));
+ QPoint tableBottomRight = d->m_eventTree->mapToGlobal(QPoint(d->m_eventTree->width(), d->m_eventTree->height()));
+ return (position.x() >= tableTopLeft.x() && position.x() <= tableBottomRight.x() && position.y() >= tableTopLeft.y() && position.y() <= tableBottomRight.y());
+}
+
+void QmlProfilerEventsWidget::copyTableToClipboard() const
+{
+ d->m_eventTree->copyTableToClipboard();
+}
+
+void QmlProfilerEventsWidget::copyRowToClipboard() const
+{
+ d->m_eventTree->copyRowToClipboard();
+}
+
+void QmlProfilerEventsWidget::updateSelectedEvent(const QString &eventHash) const
+{
+ if (d->m_eventTree->selectedEventHash() != eventHash)
+ d->m_eventTree->selectEvent(eventHash);
+}
+
+void QmlProfilerEventsWidget::selectBySourceLocation(const QString &filename, int line, int column)
+{
+ // This slot is used to connect the javascript pane with the qml events pane
+ // Our javascript trace data does not store column information
+ // thus we ignore it here
+ Q_UNUSED(column);
+ d->m_eventTree->selectEventByLocation(filename, line);
+}
+
+bool QmlProfilerEventsWidget::hasGlobalStats() const
+{
+ return d->m_globalStatsEnabled;
+}
+
+void QmlProfilerEventsWidget::setShowExtendedStatistics(bool show)
+{
+ d->m_eventTree->setShowExtendedStatistics(show);
+}
+
+bool QmlProfilerEventsWidget::showExtendedStatistics() const
+{
+ return d->m_eventTree->showExtendedStatistics();
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate
+{
+public:
+ QmlProfilerEventsMainViewPrivate(QmlProfilerEventsMainView *qq) : q(qq) {}
+
+ int getFieldCount();
+
+ QString textForItem(QStandardItem *item, bool recursive) const;
+
+
+ QmlProfilerEventsMainView *q;
+
+ QmlProfilerEventsModelProxy *modelProxy;
+ QStandardItemModel *m_model;
+ QList<bool> m_fieldShown;
+ QHash<int, int> m_columnIndex; // maps field enum to column index
+ bool m_showExtendedStatistics;
+ int m_firstNumericColumn;
+ bool m_preventSelectBounce;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+QmlProfilerEventsMainView::QmlProfilerEventsMainView(QWidget *parent,
+ QmlProfilerEventsModelProxy *modelProxy)
+: QTreeView(parent), d(new QmlProfilerEventsMainViewPrivate(this))
+{
+ setObjectName(QLatin1String("QmlProfilerEventsTable"));
+ header()->setResizeMode(QHeaderView::Interactive);
+ header()->setDefaultSectionSize(100);
+ header()->setMinimumSectionSize(50);
+ setSortingEnabled(false);
+ setFrameStyle(QFrame::NoFrame);
+
+ d->m_model = new QStandardItemModel(this);
+ setModel(d->m_model);
+ connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
+
+ d->modelProxy = modelProxy;
+ connect(d->modelProxy,SIGNAL(dataAvailable()), this, SLOT(buildModel()));
+// connect(d->modelProxy,SIGNAL(stateChanged()),
+// this,SLOT(profilerDataModelStateChanged()));
+ d->m_firstNumericColumn = 0;
+ d->m_preventSelectBounce = false;
+ d->m_showExtendedStatistics = false;
+
+ setFieldViewable(Name, true);
+ setFieldViewable(Type, true);
+ setFieldViewable(Percent, true);
+ setFieldViewable(TotalDuration, true);
+ setFieldViewable(SelfPercent, false);
+ setFieldViewable(SelfDuration, false);
+ setFieldViewable(CallCount, true);
+ setFieldViewable(TimePerCall, true);
+ setFieldViewable(MaxTime, true);
+ setFieldViewable(MinTime, true);
+ setFieldViewable(MedianTime, true);
+ setFieldViewable(Details, true);
+
+ buildModel();
+}
+
+QmlProfilerEventsMainView::~QmlProfilerEventsMainView()
+{
+ clear();
+ //delete d->modelProxy;
+ delete d->m_model;
+ delete d;
+}
+
+void QmlProfilerEventsMainView::profilerDataModelStateChanged()
+{
+}
+
+void QmlProfilerEventsMainView::setFieldViewable(Fields field, bool show)
+{
+ if (field < MaxFields) {
+ int length = d->m_fieldShown.count();
+ if (field >= length) {
+ for (int i=length; i<MaxFields; i++)
+ d->m_fieldShown << false;
+ }
+ d->m_fieldShown[field] = show;
+ }
+}
+
+
+void QmlProfilerEventsMainView::setHeaderLabels()
+{
+ int fieldIndex = 0;
+ d->m_firstNumericColumn = 0;
+
+ d->m_columnIndex.clear();
+ if (d->m_fieldShown[Name]) {
+ d->m_columnIndex[Name] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Location")));
+ d->m_firstNumericColumn++;
+ }
+ if (d->m_fieldShown[Type]) {
+ d->m_columnIndex[Type] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Type")));
+ d->m_firstNumericColumn++;
+ }
+ if (d->m_fieldShown[Percent]) {
+ d->m_columnIndex[Percent] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Time in Percent")));
+ }
+ if (d->m_fieldShown[TotalDuration]) {
+ d->m_columnIndex[TotalDuration] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Total Time")));
+ }
+ if (d->m_fieldShown[SelfPercent]) {
+ d->m_columnIndex[Type] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Self Time in Percent")));
+ }
+ if (d->m_fieldShown[SelfDuration]) {
+ d->m_columnIndex[SelfDuration] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Self Time")));
+ }
+ if (d->m_fieldShown[CallCount]) {
+ d->m_columnIndex[CallCount] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Calls")));
+ }
+ if (d->m_fieldShown[TimePerCall]) {
+ d->m_columnIndex[TimePerCall] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Mean Time")));
+ }
+ if (d->m_fieldShown[MedianTime]) {
+ d->m_columnIndex[MedianTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Median Time")));
+ }
+ if (d->m_fieldShown[MaxTime]) {
+ d->m_columnIndex[MaxTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Longest Time")));
+ }
+ if (d->m_fieldShown[MinTime]) {
+ d->m_columnIndex[MinTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Shortest Time")));
+ }
+ if (d->m_fieldShown[Details]) {
+ d->m_columnIndex[Details] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Details")));
+ }
+}
+
+void QmlProfilerEventsMainView::setShowExtendedStatistics(bool show)
+{
+ // Not checking if already set because we don't want the first call to skip
+ d->m_showExtendedStatistics = show;
+ if (show) {
+ if (d->m_fieldShown[MedianTime])
+ showColumn(d->m_columnIndex[MedianTime]);
+ if (d->m_fieldShown[MaxTime])
+ showColumn(d->m_columnIndex[MaxTime]);
+ if (d->m_fieldShown[MinTime])
+ showColumn(d->m_columnIndex[MinTime]);
+ } else{
+ if (d->m_fieldShown[MedianTime])
+ hideColumn(d->m_columnIndex[MedianTime]);
+ if (d->m_fieldShown[MaxTime])
+ hideColumn(d->m_columnIndex[MaxTime]);
+ if (d->m_fieldShown[MinTime])
+ hideColumn(d->m_columnIndex[MinTime]);
+ }
+}
+
+bool QmlProfilerEventsMainView::showExtendedStatistics() const
+{
+ return d->m_showExtendedStatistics;
+}
+
+void QmlProfilerEventsMainView::clear()
+{
+ d->m_model->clear();
+ d->m_model->setColumnCount(d->getFieldCount());
+
+ setHeaderLabels();
+ setSortingEnabled(false);
+}
+
+int QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::getFieldCount()
+{
+ int count = 0;
+ for (int i=0; i < m_fieldShown.count(); ++i)
+ if (m_fieldShown[i])
+ count++;
+ return count;
+}
+
+void QmlProfilerEventsMainView::buildModel()
+{
+ clear();
+ parseModelProxy();
+ setShowExtendedStatistics(d->m_showExtendedStatistics);
+
+ setRootIsDecorated(false);
+ setSortingEnabled(true);
+ sortByColumn(d->m_firstNumericColumn,Qt::DescendingOrder);
+
+ expandAll();
+ if (d->m_fieldShown[Name])
+ resizeColumnToContents(0);
+
+ if (d->m_fieldShown[Type])
+ resizeColumnToContents(d->m_fieldShown[Name]?1:0);
+ collapseAll();
+}
+
+void QmlProfilerEventsMainView::parseModelProxy()
+{
+ const QList <QmlProfilerEventsModelProxy::QmlEventStats> eventList = d->modelProxy->getData();
+ foreach (const QmlProfilerEventsModelProxy::QmlEventStats &event, eventList) {
+ QStandardItem *parentItem = d->m_model->invisibleRootItem();
+ QList<QStandardItem *> newRow;
+
+ if (d->m_fieldShown[Name])
+ newRow << new EventsViewItem(event.displayName);
+
+ if (d->m_fieldShown[Type]) {
+ QString typeString = QmlProfilerEventsMainView::nameForType(event.eventType);
+ QString toolTipText;
+ if (event.eventType == Binding) {
+ if (event.bindingType == (int)OptimizedBinding) {
+ typeString = typeString + tr(" (Opt)");
+ toolTipText = tr("Binding is evaluated by the optimized engine.");
+ } else if (event.bindingType == (int)V8Binding) {
+ toolTipText = tr("Binding not optimized (e.g. has side effects or assignments,\n"
+ "references to elements in other files, loops, etc.)");
+
+ }
+ }
+ newRow << new EventsViewItem(typeString);
+ newRow.last()->setData(QVariant(typeString));
+ if (!toolTipText.isEmpty())
+ newRow.last()->setToolTip(toolTipText);
+ }
+
+ if (d->m_fieldShown[Percent]) {
+ newRow << new EventsViewItem(QString::number(event.percentOfTime,'f',2)+QLatin1String(" %"));
+ newRow.last()->setData(QVariant(event.percentOfTime));
+ }
+
+ if (d->m_fieldShown[TotalDuration]) {
+ newRow << new EventsViewItem(displayTime(event.duration));
+ newRow.last()->setData(QVariant(event.duration));
+ }
+
+ if (d->m_fieldShown[CallCount]) {
+ newRow << new EventsViewItem(QString::number(event.calls));
+ newRow.last()->setData(QVariant(event.calls));
+ }
+
+ if (d->m_fieldShown[TimePerCall]) {
+ newRow << new EventsViewItem(displayTime(event.timePerCall));
+ newRow.last()->setData(QVariant(event.timePerCall));
+ }
+
+ if (d->m_fieldShown[MedianTime]) {
+ newRow << new EventsViewItem(displayTime(event.medianTime));
+ newRow.last()->setData(QVariant(event.medianTime));
+ }
+
+ if (d->m_fieldShown[MaxTime]) {
+ newRow << new EventsViewItem(displayTime(event.maxTime));
+ newRow.last()->setData(QVariant(event.maxTime));
+ }
+
+ if (d->m_fieldShown[MinTime]) {
+ newRow << new EventsViewItem(displayTime(event.minTime));
+ newRow.last()->setData(QVariant(event.minTime));
+ }
+
+ if (d->m_fieldShown[Details]) {
+ newRow << new EventsViewItem(event.details);
+ newRow.last()->setData(QVariant(event.details));
+ }
+
+
+
+ if (!newRow.isEmpty()) {
+ // no edit
+ foreach (QStandardItem *item, newRow)
+ item->setEditable(false);
+
+ // metadata
+ newRow.at(0)->setData(QVariant(event.eventHashStr),EventHashStrRole);
+ newRow.at(0)->setData(QVariant(event.location.filename),FilenameRole);
+ newRow.at(0)->setData(QVariant(event.location.line),LineRole);
+ newRow.at(0)->setData(QVariant(event.location.column),ColumnRole);
+// newRow.at(0)->setData(QVariant(event.eventId),EventIdRole);
+// if (event.isBindingLoop)
+// foreach (QStandardItem *item, newRow) {
+// item->setBackground(colors()->bindingLoopBackground);
+// item->setToolTip(tr("Binding loop detected."));
+// }
+
+ // append
+ parentItem->appendRow(newRow);
+ }
+ }
+}
+
+QString QmlProfilerEventsMainView::displayTime(double time)
+{
+ if (time < 1e6)
+ return QString::number(time/1e3,'f',3) + trUtf8(" \xc2\xb5s");
+ if (time < 1e9)
+ return QString::number(time/1e6,'f',3) + tr(" ms");
+
+ return QString::number(time/1e9,'f',3) + tr(" s");
+}
+
+QString QmlProfilerEventsMainView::nameForType(int typeNumber)
+{
+ switch (typeNumber) {
+ case 0: return QmlProfilerEventsMainView::tr("Paint");
+ case 1: return QmlProfilerEventsMainView::tr("Compile");
+ case 2: return QmlProfilerEventsMainView::tr("Create");
+ case 3: return QmlProfilerEventsMainView::tr("Binding");
+ case 4: return QmlProfilerEventsMainView::tr("Signal");
+ }
+ return QString();
+}
+
+void QmlProfilerEventsMainView::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
+{
+}
+
+bool QmlProfilerEventsMainView::isRangeGlobal(qint64 rangeStart, qint64 rangeEnd) const
+{
+}
+
+const QString &QmlProfilerEventsMainView::selectedEventHash() const
+{
+ QModelIndex index = selectedItem();
+ if (!index.isValid())
+ return QString();
+ QStandardItem *item = d->m_model->item(index.row(), 0);
+ return item->data(EventHashStrRole).toString();
+}
+
+
+void QmlProfilerEventsMainView::jumpToItem(const QModelIndex &index)
+{
+ if (d->m_preventSelectBounce)
+ return;
+
+ d->m_preventSelectBounce = true;
+ QStandardItem *clickedItem = d->m_model->itemFromIndex(index);
+ QStandardItem *infoItem;
+ if (clickedItem->parent())
+ infoItem = clickedItem->parent()->child(clickedItem->row(), 0);
+ else
+ infoItem = d->m_model->item(index.row(), 0);
+
+ // show in editor
+ int line = infoItem->data(LineRole).toInt();
+ int column = infoItem->data(ColumnRole).toInt();
+ QString fileName = infoItem->data(FilenameRole).toString();
+ if (line!=-1 && !fileName.isEmpty())
+ emit gotoSourceLocation(fileName, line, column);
+
+ // show in callers/callees subwindow
+ emit eventSelected(infoItem->data(EventHashStrRole).toString());
+
+ // show in timelinerenderer
+ emit showEventInTimeline(infoItem->data(EventIdRole).toInt());
+
+ d->m_preventSelectBounce = false;
+}
+
+void QmlProfilerEventsMainView::selectEvent(const QString &eventHash)
+{
+ for (int i=0; i<d->m_model->rowCount(); i++) {
+ QStandardItem *infoItem = d->m_model->item(i, 0);
+ if (infoItem->data(EventHashStrRole).toString() == eventHash) {
+ setCurrentIndex(d->m_model->indexFromItem(infoItem));
+ jumpToItem(currentIndex());
+ return;
+ }
+ }
+}
+
+void QmlProfilerEventsMainView::selectEventByLocation(const QString &filename, int line)
+{
+ if (d->m_preventSelectBounce)
+ return;
+
+ for (int i=0; i<d->m_model->rowCount(); i++) {
+ QStandardItem *infoItem = d->m_model->item(i, 0);
+ if (currentIndex() != d->m_model->indexFromItem(infoItem) && infoItem->data(FilenameRole).toString() == filename && infoItem->data(LineRole).toInt() == line) {
+ setCurrentIndex(d->m_model->indexFromItem(infoItem));
+ jumpToItem(currentIndex());
+ return;
+ }
+ }
+}
+
+QModelIndex QmlProfilerEventsMainView::selectedItem() const
+{
+ QModelIndexList sel = selectedIndexes();
+ if (sel.isEmpty())
+ return QModelIndex();
+ else
+ return sel.first();
+}
+
+void QmlProfilerEventsMainView::changeDetailsForEvent(int eventId, const QString &newString)
+{
+ for (int i=0; i<d->m_model->rowCount(); i++) {
+ QStandardItem *infoItem = d->m_model->item(i, 0);
+ if (infoItem->data(EventIdRole).toInt() == eventId) {
+ d->m_model->item(i,d->m_columnIndex[Details])->setData(QVariant(newString),Qt::DisplayRole);
+ d->m_model->item(i,d->m_columnIndex[Details])->setData(QVariant(newString));
+ return;
+ }
+ }
+}
+
+QString QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::textForItem(QStandardItem *item, bool recursive = true) const
+{
+ QString str;
+
+ if (recursive) {
+ // indentation
+ QStandardItem *itemParent = item->parent();
+ while (itemParent) {
+ str += QLatin1String(" ");
+ itemParent = itemParent->parent();
+ }
+ }
+
+ // item's data
+ int colCount = m_model->columnCount();
+ for (int j = 0; j < colCount; ++j) {
+ QStandardItem *colItem = item->parent() ? item->parent()->child(item->row(),j) : m_model->item(item->row(),j);
+ str += colItem->data(Qt::DisplayRole).toString();
+ if (j < colCount-1) str += QLatin1Char('\t');
+ }
+ str += QLatin1Char('\n');
+
+ // recursively print children
+ if (recursive && item->child(0))
+ for (int j = 0; j != item->rowCount(); j++)
+ str += textForItem(item->child(j));
+
+ return str;
+}
+
+void QmlProfilerEventsMainView::copyTableToClipboard() const
+{
+ QString str;
+ // headers
+ int columnCount = d->m_model->columnCount();
+ for (int i = 0; i < columnCount; ++i) {
+ str += d->m_model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
+ if (i < columnCount - 1)
+ str += QLatin1Char('\t');
+ else
+ str += QLatin1Char('\n');
+ }
+ // data
+ int rowCount = d->m_model->rowCount();
+ for (int i = 0; i != rowCount; ++i) {
+ str += d->textForItem(d->m_model->item(i));
+ }
+ QClipboard *clipboard = QApplication::clipboard();
+ clipboard->setText(str, QClipboard::Selection);
+ clipboard->setText(str, QClipboard::Clipboard);
+}
+
+void QmlProfilerEventsMainView::copyRowToClipboard() const
+{
+ QString str;
+ str = d->textForItem(d->m_model->itemFromIndex(selectedItem()), false);
+
+ QClipboard *clipboard = QApplication::clipboard();
+ clipboard->setText(str, QClipboard::Selection);
+ clipboard->setText(str, QClipboard::Clipboard);
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+#ifdef SEE_CHILDREN
+
+QmlProfilerEventsParentsAndChildrenView::~QmlProfilerEventsParentsAndChildrenView()
+{
+}
+
+void QmlProfilerEventsParentsAndChildrenView::setViewType(SubViewType type)
+{
+ m_subtableType = type;
+ updateHeader();
+}
+
+void QmlProfilerEventsParentsAndChildrenView::displayEvent(int eventId)
+{
+
+}
+
+void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *profilerDataModel)
+{
+}
+
+void QmlProfilerEventsParentsAndChildrenView::clear()
+{
+ if (treeModel()) {
+ treeModel()->clear();
+ updateHeader();
+ }
+}
+
+void QmlProfilerEventsParentsAndChildrenView::updateHeader()
+{
+ bool isV8 = m_subtableType == V8ParentsView || m_subtableType == V8ChildrenView;
+ bool isChildren = m_subtableType == ChildrenView || m_subtableType == V8ChildrenView;
+
+ header()->setResizeMode(QHeaderView::Interactive);
+ header()->setDefaultSectionSize(100);
+ header()->setMinimumSectionSize(50);
+
+ if (treeModel()) {
+ if (isV8)
+ treeModel()->setColumnCount(3);
+ else
+ treeModel()->setColumnCount(5);
+
+ int columnIndex = 0;
+ if (isChildren)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee")));
+ else
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller")));
+
+ if (!isV8)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Type")));
+
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Total Time")));
+
+ if (!isV8)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Calls")));
+
+ if (isChildren)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee Description")));
+ else
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller Description")));
+ }
+}
+
+QStandardItemModel *QmlProfilerEventsParentsAndChildrenView::treeModel()
+{
+ return qobject_cast<QStandardItemModel *>(model());
+}
+
+void QmlProfilerEventsParentsAndChildrenView::jumpToItem(const QModelIndex &index)
+{
+ if (treeModel()) {
+ QStandardItem *infoItem = treeModel()->item(index.row(), 0);
+ emit eventClicked(infoItem->data(EventIdRole).toInt());
+ }
+}
+#else
+class QmlProfilerEventRelativesView::QmlProfilerEventParentsViewPrivate
+{
+public:
+ QmlProfilerEventParentsViewPrivate(QmlProfilerEventRelativesView *qq):q(qq) {}
+ ~QmlProfilerEventParentsViewPrivate() {}
+
+ QmlProfilerEventRelativesModelProxy *modelProxy;
+
+ QmlProfilerEventRelativesView *q;
+};
+
+QmlProfilerEventRelativesView::QmlProfilerEventRelativesView(QmlProfilerModelManager *modelManager, QmlProfilerEventRelativesModelProxy *modelProxy, QWidget *parent)
+ : QTreeView(parent), d(new QmlProfilerEventParentsViewPrivate(this))
+{
+ Q_UNUSED(modelManager);
+ //m_profilerModelManager = modelManager;
+ d->modelProxy = modelProxy; //new QmlProfilerEventParentsModelProxy(modelManager, this);
+ setModel(new QStandardItemModel(this));
+ setRootIsDecorated(false);
+ setFrameStyle(QFrame::NoFrame);
+ updateHeader();
+
+ connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
+}
+
+QmlProfilerEventRelativesView::~QmlProfilerEventRelativesView()
+{
+ //delete d->modelProxy;
+ delete d;
+}
+
+void QmlProfilerEventRelativesView::displayEvent(const QString &eventHash)
+{
+ // TODO: what if it's not there?
+// QmlProfilerEventParentsModelProxy::QmlEventLinks qmlEvent = d->modelProxy->getData(eventHash);
+ rebuildTree(d->modelProxy->getData(eventHash));
+
+ updateHeader();
+ resizeColumnToContents(0);
+ setSortingEnabled(true);
+ sortByColumn(2);
+}
+
+void QmlProfilerEventRelativesView::rebuildTree(QmlProfilerEventRelativesModelProxy::QmlEventRelativesMap eventMap)
+{
+ Q_ASSERT(treeModel());
+ treeModel()->clear();
+
+ QStandardItem *topLevelItem = treeModel()->invisibleRootItem();
+
+ //foreach (const QmlProfilerEventParentsModelProxy::QmlEventParentData &event, eventMap.values()) {
+ foreach (const QString &key, eventMap.keys()) {
+ const QmlProfilerEventRelativesModelProxy::QmlEventRelativesData &event = eventMap[key];
+ QList<QStandardItem *> newRow;
+
+ // ToDo: here we were going to search for the data in the other modelproxy
+ // maybe we should store the data in this proxy and get it here
+ // no indirections at this level of abstraction!
+ newRow << new EventsViewItem(event.displayName);
+ newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event.eventType));
+ newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event.duration));
+ newRow << new EventsViewItem(QString::number(event.calls));
+ newRow << new EventsViewItem(event.details);
+
+// newRow << new EventsViewItem(event->reference->displayName);
+// newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event->reference->eventType));
+// newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event->duration));
+// newRow << new EventsViewItem(QString::number(event->calls));
+// newRow << new EventsViewItem(event->reference->details);
+ newRow.at(0)->setData(QVariant(key), EventHashStrRole);
+// newRow.at(0)->setData(QVariant(event->reference->eventId), EventIdRole);
+ newRow.at(2)->setData(QVariant(event.duration));
+ newRow.at(3)->setData(QVariant(event.calls));
+// if (event->inLoopPath)
+// foreach (QStandardItem *item, newRow) {
+// item->setBackground(colors()->bindingLoopBackground);
+// item->setToolTip(tr("Part of binding loop."));
+// }
+
+ foreach (QStandardItem *item, newRow)
+ item->setEditable(false);
+
+ topLevelItem->appendRow(newRow);
+ }
+}
+
+void QmlProfilerEventRelativesView::clear()
+{
+ if (treeModel()) {
+ treeModel()->clear();
+ updateHeader();
+ }
+}
+
+void QmlProfilerEventRelativesView::updateHeader()
+{
+ bool calleesView = qobject_cast<QmlProfilerEventChildrenModelProxy *>(d->modelProxy) != 0;
+
+ header()->setResizeMode(QHeaderView::Interactive);
+ header()->setDefaultSectionSize(100);
+ header()->setMinimumSectionSize(50);
+
+ if (treeModel()) {
+ treeModel()->setColumnCount(5);
+
+ int columnIndex = 0;
+ if (calleesView)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee")));
+ else
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller")));
+
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Type")));
+
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Total Time")));
+
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Calls")));
+
+
+ if (calleesView)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee Description")));
+ else
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller Description")));
+ }
+}
+
+QStandardItemModel *QmlProfilerEventRelativesView::treeModel()
+{
+ return qobject_cast<QStandardItemModel *>(model());
+}
+
+void QmlProfilerEventRelativesView::jumpToItem(const QModelIndex &index)
+{
+ if (treeModel()) {
+ QStandardItem *infoItem = treeModel()->item(index.row(), 0);
+ emit eventClicked(infoItem->data(EventHashStrRole).toString());
+ }
+}
+
+#endif
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/plugins/qmlprofiler/qmlprofilerv8eventsview.h b/plugins/qmlprofiler/qmlprofilerv8eventsview.h
new file mode 100644
index 0000000000..c15443b7f2
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerv8eventsview.h
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILEREVENTVIEW_H
+#define QMLPROFILEREVENTVIEW_H
+
+#include <QTreeView>
+#include <QStandardItemModel>
+#include <qmldebug/qmlprofilereventtypes.h>
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilereventsmodelproxy.h"
+
+#include <analyzerbase/ianalyzertool.h>
+
+#include "qmlprofilerviewmanager.h"
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerEventsMainView;
+class QmlProfilerEventChildrenView;
+class QmlProfilerEventRelativesView;
+
+enum ItemRole {
+ EventHashStrRole = Qt::UserRole+1,
+ FilenameRole = Qt::UserRole+2,
+ LineRole = Qt::UserRole+3,
+ ColumnRole = Qt::UserRole+4,
+ EventIdRole = Qt::UserRole+5
+};
+
+class QmlProfilerEventsWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit QmlProfilerEventsWidget(QWidget *parent,
+ Analyzer::IAnalyzerTool *profilerTool,
+ QmlProfilerViewManager *container,
+ QmlProfilerModelManager *profilerModelManager );
+ ~QmlProfilerEventsWidget();
+
+ void clear();
+
+ void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
+ QModelIndex selectedItem() const;
+ bool mouseOnTable(const QPoint &position) const;
+ void copyTableToClipboard() const;
+ void copyRowToClipboard() const;
+
+ bool hasGlobalStats() const;
+ void setShowExtendedStatistics(bool show);
+ bool showExtendedStatistics() const;
+
+
+signals:
+ void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
+ void showEventInTimeline(int eventId);
+ void resized();
+
+public slots:
+ void updateSelectedEvent(const QString &eventHash) const;
+ void selectBySourceLocation(const QString &filename, int line, int column);
+
+private slots:
+ void profilerDataModelStateChanged();
+
+protected:
+ void contextMenuEvent(QContextMenuEvent *ev);
+ virtual void resizeEvent(QResizeEvent *event);
+
+private:
+ class QmlProfilerEventsWidgetPrivate;
+ QmlProfilerEventsWidgetPrivate *d;
+};
+
+class QmlProfilerEventsMainView : public QTreeView
+{
+ Q_OBJECT
+public:
+ enum Fields {
+ Name,
+ Type,
+ Percent,
+ TotalDuration,
+ SelfPercent,
+ SelfDuration,
+ CallCount,
+ TimePerCall,
+ MaxTime,
+ MinTime,
+ MedianTime,
+ Details,
+
+ MaxFields
+ };
+
+ explicit QmlProfilerEventsMainView(QWidget *parent,
+ QmlProfilerEventsModelProxy *modelProxy);
+ ~QmlProfilerEventsMainView();
+
+ void setFieldViewable(Fields field, bool show);
+ void setShowAnonymousEvents( bool showThem );
+
+ QModelIndex selectedItem() const;
+ void copyTableToClipboard() const;
+ void copyRowToClipboard() const;
+
+ static QString displayTime(double time);
+ static QString nameForType(int typeNumber);
+
+ void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
+ bool isRangeGlobal(qint64 rangeStart, qint64 rangeEnd) const;
+// int selectedEventId() const;
+ const QString &selectedEventHash() const;
+
+ void setShowExtendedStatistics(bool);
+ bool showExtendedStatistics() const;
+
+
+signals:
+ void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
+ void eventSelected(const QString &eventHash);
+ void showEventInTimeline(int eventId);
+
+public slots:
+ void clear();
+ void jumpToItem(const QModelIndex &index);
+ void selectEvent(const QString &eventHash);
+ void selectEventByLocation(const QString &filename, int line);
+ void buildModel();
+ void changeDetailsForEvent(int eventId, const QString &newString);
+
+private slots:
+ void profilerDataModelStateChanged();
+
+private:
+ void setHeaderLabels();
+ void parseModelProxy();
+
+private:
+ class QmlProfilerEventsMainViewPrivate;
+ QmlProfilerEventsMainViewPrivate *d;
+
+};
+
+class QmlProfilerEventRelativesView : public QTreeView
+{
+ Q_OBJECT
+public:
+ explicit QmlProfilerEventRelativesView(QmlProfilerModelManager *modelManager,
+ QmlProfilerEventRelativesModelProxy *modelProxy,
+ QWidget *parent );
+ ~QmlProfilerEventRelativesView();
+
+signals:
+ void eventClicked(const QString &eventHash);
+
+public slots:
+ void displayEvent(const QString &eventHash);
+ void jumpToItem(const QModelIndex &);
+ void clear();
+
+private:
+ void rebuildTree(QmlProfilerEventParentsModelProxy::QmlEventRelativesMap eventMap);
+ void updateHeader();
+ QStandardItemModel *treeModel();
+// QmlProfilerModelManager *m_profilerModelManager;
+
+ class QmlProfilerEventParentsViewPrivate;
+ QmlProfilerEventParentsViewPrivate *d;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILEREVENTVIEW_H
diff --git a/plugins/qmlprofiler/qmlprofilerviewmanager.cpp b/plugins/qmlprofiler/qmlprofilerviewmanager.cpp
new file mode 100644
index 0000000000..00496107c6
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerviewmanager.cpp
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilerviewmanager.h"
+
+#include "qmlprofilertraceview.h"
+#include "qmlprofilereventview.h"
+#include "qmlprofilertool.h"
+#include "qmlprofilerstatemanager.h"
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilerstatewidget.h"
+#include "qv8profilereventview.h"
+
+#include <utils/qtcassert.h>
+#include <utils/fancymainwindow.h>
+#include <analyzerbase/analyzermanager.h>
+
+#include <QDockWidget>
+
+using namespace Analyzer;
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerViewManager::QmlProfilerViewManagerPrivate {
+public:
+ QmlProfilerViewManagerPrivate(QmlProfilerViewManager *qq) { Q_UNUSED(qq); }
+
+ QmlProfilerTraceView *traceView;
+ QmlProfilerEventsWidget *eventsView;
+ QV8ProfilerEventsWidget *v8profilerView;
+ QmlProfilerStateManager *profilerState;
+ QmlProfilerModelManager *profilerModelManager;
+ QmlProfilerTool *profilerTool;
+};
+
+QmlProfilerViewManager::QmlProfilerViewManager(QObject *parent,
+ QmlProfilerTool *profilerTool,
+ QmlProfilerModelManager *modelManager,
+ QmlProfilerStateManager *profilerState)
+ : QObject(parent), d(new QmlProfilerViewManagerPrivate(this))
+{
+ setObjectName(QLatin1String("QML Profiler View Manager"));
+ d->traceView = 0;
+ d->eventsView = 0;
+ d->v8profilerView = 0;
+ d->profilerState = profilerState;
+ d->profilerModelManager = modelManager;
+ d->profilerTool = profilerTool;
+ createViews();
+}
+
+QmlProfilerViewManager::~QmlProfilerViewManager()
+{
+ delete d;
+}
+
+////////////////////////////////////////////////////////////
+// Views
+void QmlProfilerViewManager::createViews()
+{
+
+ QTC_ASSERT(d->profilerModelManager, return);
+ QTC_ASSERT(d->profilerState, return);
+
+ Utils::FancyMainWindow *mw = AnalyzerManager::mainWindow();
+
+ d->traceView = new QmlProfilerTraceView(mw,
+ d->profilerTool,
+ this,
+ d->profilerModelManager,
+ d->profilerState);
+ connect(d->traceView, SIGNAL(gotoSourceLocation(QString,int,int)),
+ this, SIGNAL(gotoSourceLocation(QString,int,int)));
+ d->traceView->reset();
+
+
+ d->eventsView = new QmlProfilerEventsWidget(mw, d->profilerTool, this,
+ d->profilerModelManager);
+ connect(d->eventsView, SIGNAL(gotoSourceLocation(QString,int,int)), this,
+ SIGNAL(gotoSourceLocation(QString,int,int)));
+ connect(d->eventsView, SIGNAL(showEventInTimeline(int)), d->traceView,
+ SLOT(selectNextEventWithId(int)));
+
+ d->v8profilerView = new QV8ProfilerEventsWidget(mw, d->profilerTool, this,
+ d->profilerModelManager);
+
+ QDockWidget *eventsDock = AnalyzerManager::createDockWidget
+ (d->profilerTool, tr("Events"), d->eventsView, Qt::BottomDockWidgetArea);
+ QDockWidget *timelineDock = AnalyzerManager::createDockWidget
+ (d->profilerTool, tr("Timeline"), d->traceView, Qt::BottomDockWidgetArea);
+ QDockWidget *v8profilerDock = AnalyzerManager::createDockWidget(
+ d->profilerTool, tr("JavaScript"), d->v8profilerView, Qt::BottomDockWidgetArea);
+
+ eventsDock->show();
+ timelineDock->show();
+ v8profilerDock->show();
+
+ mw->splitDockWidget(mw->toolBarDockWidget(), timelineDock, Qt::Vertical);
+ mw->tabifyDockWidget(timelineDock, eventsDock);
+ mw->tabifyDockWidget(eventsDock, v8profilerDock);
+
+ new QmlProfilerStateWidget(d->profilerState, d->profilerModelManager, d->eventsView);
+ new QmlProfilerStateWidget(d->profilerState, d->profilerModelManager, d->traceView);
+ new QmlProfilerStateWidget(d->profilerState, d->profilerModelManager, d->v8profilerView);
+}
+
+bool QmlProfilerViewManager::hasValidSelection() const
+{
+ return d->traceView->hasValidSelection();
+}
+
+qint64 QmlProfilerViewManager::selectionStart() const
+{
+ return d->traceView->selectionStart();
+}
+
+qint64 QmlProfilerViewManager::selectionEnd() const
+{
+ return d->traceView->selectionEnd();
+}
+
+bool QmlProfilerViewManager::hasGlobalStats() const
+{
+ return d->eventsView->hasGlobalStats();
+}
+
+void QmlProfilerViewManager::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
+{
+ d->eventsView->getStatisticsInRange(rangeStart, rangeEnd);
+}
+
+void QmlProfilerViewManager::clear()
+{
+ d->traceView->clearDisplay();
+ d->eventsView->clear();
+ d->v8profilerView->clear();
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/plugins/qmlprofiler/qmlprofilerviewmanager.h b/plugins/qmlprofiler/qmlprofilerviewmanager.h
new file mode 100644
index 0000000000..f753f52d2a
--- /dev/null
+++ b/plugins/qmlprofiler/qmlprofilerviewmanager.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERVIEWMANAGER_H
+#define QMLPROFILERVIEWMANAGER_H
+
+#include <QObject>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerTool;
+class QmlProfilerModelManager;
+class QmlProfilerStateManager;
+
+class QmlProfilerViewManager : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QmlProfilerViewManager(QObject *parent,
+ QmlProfilerTool *profilerTool,
+ QmlProfilerModelManager *modelManager,
+ QmlProfilerStateManager *profilerState);
+ ~QmlProfilerViewManager();
+
+ void createViews();
+
+ // used by the options "limit events to range"
+ bool hasValidSelection() const;
+ qint64 selectionStart() const;
+ qint64 selectionEnd() const;
+ bool hasGlobalStats() const;
+ void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
+
+public slots:
+ void clear();
+
+signals:
+ void gotoSourceLocation(QString,int,int);
+
+private:
+ class QmlProfilerViewManagerPrivate;
+ QmlProfilerViewManagerPrivate *d;
+};
+
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILERVIEWMANAGER_H
diff --git a/plugins/qmlprofiler/qv8profilerdatamodel.cpp b/plugins/qmlprofiler/qv8profilerdatamodel.cpp
new file mode 100644
index 0000000000..ac90fd00e0
--- /dev/null
+++ b/plugins/qmlprofiler/qv8profilerdatamodel.cpp
@@ -0,0 +1,495 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qv8profilerdatamodel.h"
+
+#include <QStringList>
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QV8EventData, Q_MOVABLE_TYPE);
+Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QV8EventSub, Q_MOVABLE_TYPE);
+QT_END_NAMESPACE
+
+namespace QmlProfiler {
+namespace Internal {
+
+typedef QHash <QString, QV8EventSub *> EventHash;
+
+static EventHash cloneEventHash(const EventHash &src)
+{
+ EventHash result;
+ const EventHash::ConstIterator cend = src.constEnd();
+ for (EventHash::ConstIterator it = src.constBegin(); it != cend; ++it)
+ result.insert(it.key(), new QV8EventSub(it.value()));
+ return result;
+}
+
+QV8EventData &QV8EventData::operator=(const QV8EventData &ref)
+{
+ if (this == &ref)
+ return *this;
+
+ displayName = ref.displayName;
+ eventHashStr = ref.eventHashStr;
+ 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);
+ parentHash = cloneEventHash(ref.parentHash);
+
+ qDeleteAll(childrenHash);
+ childrenHash = cloneEventHash(ref.childrenHash);
+
+ return *this;
+}
+
+QV8EventData::QV8EventData()
+{
+ line = -1;
+ eventId = -1;
+ totalTime = 0;
+ selfTime = 0;
+ totalPercent = 0;
+ selfPercent = 0;
+}
+
+QV8EventData::~QV8EventData()
+{
+ qDeleteAll(parentHash.values());
+ parentHash.clear();
+ qDeleteAll(childrenHash.values());
+ childrenHash.clear();
+}
+
+class QV8ProfilerDataModel::QV8ProfilerDataModelPrivate
+{
+public:
+ QV8ProfilerDataModelPrivate(QV8ProfilerDataModel *qq) {Q_UNUSED(qq);}
+
+ void clearV8RootEvent();
+ void collectV8Statistics();
+
+ QHash<QString, QV8EventData *> v8EventHash;
+ QHash<int, QV8EventData *> v8parents;
+ QV8EventData v8RootEvent;
+ qint64 v8MeasuredTime;
+};
+
+QV8ProfilerDataModel::QV8ProfilerDataModel(QObject *parent)
+ : QObject(parent)
+ , d(new QV8ProfilerDataModelPrivate(this))
+{
+ d->v8MeasuredTime = 0;
+ d->clearV8RootEvent();
+}
+
+QV8ProfilerDataModel::~QV8ProfilerDataModel()
+{
+ delete d;
+}
+
+void QV8ProfilerDataModel::clear()
+{
+ qDeleteAll(d->v8EventHash.values());
+ d->v8EventHash.clear();
+ d->v8parents.clear();
+ d->clearV8RootEvent();
+ d->v8MeasuredTime = 0;
+
+ emit changed();
+}
+
+bool QV8ProfilerDataModel::isEmpty() const
+{
+ return d->v8EventHash.isEmpty();
+}
+
+QV8EventData *QV8ProfilerDataModel::v8EventDescription(int eventId) const
+{
+ foreach (QV8EventData *event, d->v8EventHash.values()) {
+ if (event->eventId == eventId)
+ return event;
+ }
+ return 0;
+}
+
+qint64 QV8ProfilerDataModel::v8MeasuredTime() const
+{
+ return d->v8MeasuredTime;
+}
+
+QList<QV8EventData *> QV8ProfilerDataModel::getV8Events() const
+{
+ return d->v8EventHash.values();
+}
+
+QString getHashStringForV8Event(const QString &displayName, const QString &function)
+{
+ return QString::fromLatin1("%1:%2").arg(displayName, function);
+}
+
+void QV8ProfilerDataModel::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);
+
+ // time is given in milliseconds, but internally we store it in microseconds
+ totalTime *= 1e6;
+ selfTime *= 1e6;
+
+ // accumulate information
+ QV8EventData *eventData = d->v8EventHash[hashStr];
+ if (!eventData) {
+ eventData = new QV8EventData;
+ 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;
+
+ QV8EventData *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 (!eventData->parentHash.contains(parentEvent->eventHashStr)) {
+ QV8EventSub *newParentSub = new QV8EventSub(parentEvent);
+ newParentSub->totalTime = totalTime;
+
+ eventData->parentHash.insert(parentEvent->eventHashStr, newParentSub);
+ } else {
+ QV8EventSub *newParentSub = eventData->parentHash.value(parentEvent->eventHashStr);
+ newParentSub->totalTime += totalTime;
+ }
+
+ if (!parentEvent->childrenHash.contains(eventData->eventHashStr)) {
+ QV8EventSub *newChildSub = new QV8EventSub(eventData);
+ newChildSub->totalTime = totalTime;
+
+ parentEvent->childrenHash.insert(eventData->eventHashStr, newChildSub);
+ } else {
+ QV8EventSub *newChildSub = parentEvent->childrenHash.value(eventData->eventHashStr);
+ newChildSub->totalTime += totalTime;
+ }
+ }
+
+}
+
+void QV8ProfilerDataModel::collectV8Statistics()
+{
+ d->collectV8Statistics();
+}
+
+void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::collectV8Statistics()
+{
+ if (!v8EventHash.isEmpty()) {
+ double totalTimes = v8MeasuredTime;
+ double selfTimes = 0;
+ foreach (QV8EventData *v8event, v8EventHash.values()) {
+ 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
+ v8RootEvent.totalTime = v8MeasuredTime + 1;
+ v8RootEvent.selfTime = 0;
+
+ QString rootEventHash = getHashStringForV8Event(
+ tr("<program>"),
+ tr("Main Program"));
+ QV8EventData *v8RootEventPointer = v8EventHash[rootEventHash];
+ if (v8RootEventPointer) {
+ v8RootEvent = *v8RootEventPointer;
+ } else {
+ v8EventHash[rootEventHash] = new QV8EventData;
+ *v8EventHash[rootEventHash] = v8RootEvent;
+ }
+
+ foreach (QV8EventData *v8event, v8EventHash.values()) {
+ v8event->totalPercent = v8event->totalTime * 100.0 / totalTimes;
+ v8event->selfPercent = v8event->selfTime * 100.0 / selfTimes;
+ }
+
+ int index = 0;
+ foreach (QV8EventData *v8event, v8EventHash.values()) {
+ v8event->eventId = index++;
+ }
+ v8RootEvent.eventId = v8EventHash[rootEventHash]->eventId;
+ } else {
+ // On empty data, still add a fake root event
+ clearV8RootEvent();
+ v8RootEvent.totalPercent = 100;
+ QString rootEventHash = getHashStringForV8Event(
+ tr("<program>"),
+ tr("Main Program"));
+ v8EventHash[rootEventHash] = new QV8EventData;
+ *v8EventHash[rootEventHash] = v8RootEvent;
+ }
+}
+
+void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::clearV8RootEvent()
+{
+ v8RootEvent.displayName = tr("<program>");
+ v8RootEvent.eventHashStr = tr("<program>");
+ v8RootEvent.functionName = tr("Main Program");
+
+ v8RootEvent.line = -1;
+ v8RootEvent.totalTime = 0;
+ v8RootEvent.totalPercent = 0;
+ v8RootEvent.selfTime = 0;
+ v8RootEvent.selfPercent = 0;
+ v8RootEvent.eventId = -1;
+
+ qDeleteAll(v8RootEvent.parentHash.values());
+ qDeleteAll(v8RootEvent.childrenHash.values());
+ v8RootEvent.parentHash.clear();
+ v8RootEvent.childrenHash.clear();
+}
+
+void QV8ProfilerDataModel::save(QXmlStreamWriter &stream)
+{
+ stream.writeStartElement(QLatin1String("v8profile")); // v8 profiler output
+ stream.writeAttribute(QLatin1String("totalTime"), QString::number(d->v8MeasuredTime));
+ foreach (QV8EventData *v8event, d->v8EventHash.values()) {
+ stream.writeStartElement(QLatin1String("event"));
+ stream.writeAttribute(QLatin1String("index"),
+ QString::number(
+ d->v8EventHash.keys().indexOf(
+ v8event->eventHashStr)));
+ stream.writeTextElement(QLatin1String("displayname"), v8event->displayName);
+ stream.writeTextElement(QLatin1String("functionname"), v8event->functionName);
+ if (!v8event->filename.isEmpty()) {
+ stream.writeTextElement(QLatin1String("filename"), v8event->filename);
+ stream.writeTextElement(QLatin1String("line"), QString::number(v8event->line));
+ }
+ stream.writeTextElement(QLatin1String("totalTime"), QString::number(v8event->totalTime));
+ stream.writeTextElement(QLatin1String("selfTime"), QString::number(v8event->selfTime));
+ if (!v8event->childrenHash.isEmpty()) {
+ stream.writeStartElement(QLatin1String("childrenEvents"));
+ QStringList childrenIndexes;
+ QStringList childrenTimes;
+ QStringList parentTimes;
+ foreach (QV8EventSub *v8child, v8event->childrenHash.values()) {
+ childrenIndexes << QString::number(v8child->reference->eventId);
+ childrenTimes << QString::number(v8child->totalTime);
+ parentTimes << QString::number(v8child->totalTime);
+ }
+
+ stream.writeAttribute(QLatin1String("list"), childrenIndexes.join(QLatin1String(", ")));
+ stream.writeAttribute(QLatin1String("childrenTimes"), childrenTimes.join(QLatin1String(", ")));
+ stream.writeAttribute(QLatin1String("parentTimes"), parentTimes.join(QLatin1String(", ")));
+ stream.writeEndElement();
+ }
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // v8 profiler output
+}
+
+void QV8ProfilerDataModel::load(QXmlStreamReader &stream)
+{
+ QHash <int, QV8EventData *> v8eventBuffer;
+ QHash <int, QString> childrenIndexes;
+ QHash <int, QString> childrenTimes;
+ QHash <int, QString> parentTimes;
+ QV8EventData *v8event = 0;
+
+ // time computation
+ d->v8MeasuredTime = 0;
+ double cumulatedV8Time = 0;
+
+ // get the v8 time
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute(QLatin1String("totalTime")))
+ d->v8MeasuredTime = attributes.value(QLatin1String("totalTime")).toString().toDouble();
+
+ while (!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 == QLatin1String("event")) {
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute(QLatin1String("index"))) {
+ int ndx = attributes.value(QLatin1String("index")).toString().toInt();
+ if (!v8eventBuffer.value(ndx))
+ v8eventBuffer[ndx] = new QV8EventData;
+ v8event = v8eventBuffer[ndx];
+ } else {
+ v8event = 0;
+ }
+ break;
+ }
+
+ if (!v8event)
+ break;
+
+ if (elementName == QLatin1String("childrenEvents")) {
+ QXmlStreamAttributes attributes = stream.attributes();
+ int eventIndex = v8eventBuffer.key(v8event);
+ if (attributes.hasAttribute(QLatin1String("list"))) {
+ // store for later parsing (we haven't read all the events yet)
+ childrenIndexes[eventIndex] = attributes.value(QLatin1String("list")).toString();
+ }
+ if (attributes.hasAttribute(QLatin1String("childrenTimes"))) {
+ childrenTimes[eventIndex] =
+ attributes.value(QLatin1String("childrenTimes")).toString();
+ }
+ if (attributes.hasAttribute(QLatin1String("parentTimes")))
+ parentTimes[eventIndex] = attributes.value(QLatin1String("parentTimes")).toString();
+ }
+
+ stream.readNext();
+ if (stream.tokenType() != QXmlStreamReader::Characters)
+ break;
+ QString readData = stream.text().toString();
+
+ if (elementName == QLatin1String("displayname")) {
+ v8event->displayName = readData;
+ break;
+ }
+
+ if (elementName == QLatin1String("functionname")) {
+ v8event->functionName = readData;
+ break;
+ }
+
+ if (elementName == QLatin1String("filename")) {
+ v8event->filename = readData;
+ break;
+ }
+
+ if (elementName == QLatin1String("line")) {
+ v8event->line = readData.toInt();
+ break;
+ }
+
+ if (elementName == QLatin1String("totalTime")) {
+ v8event->totalTime = readData.toDouble();
+ cumulatedV8Time += v8event->totalTime;
+ break;
+ }
+
+ if (elementName == QLatin1String("selfTime")) {
+ v8event->selfTime = readData.toDouble();
+ break;
+ }
+ break;
+ }
+ case QXmlStreamReader::EndElement : {
+ if (elementName == QLatin1String("v8profile")) {
+ // done reading the v8 profile data
+ break;
+ }
+ }
+ default: break;
+ }
+ }
+
+ // backwards compatibility
+ if (d->v8MeasuredTime == 0)
+ d->v8MeasuredTime = cumulatedV8Time;
+
+ // find v8events' children and parents
+ typedef QHash <int, QString>::ConstIterator ChildIndexConstIt;
+
+ const ChildIndexConstIt icend = childrenIndexes.constEnd();
+ for (ChildIndexConstIt it = childrenIndexes.constBegin(); it != icend; ++it) {
+ const int parentIndex = it.key();
+ const QStringList childrenStrings = it.value().split(QLatin1Char(','));
+ QStringList childrenTimesStrings = childrenTimes.value(parentIndex).split(QLatin1String(", "));
+ QStringList parentTimesStrings = parentTimes.value(parentIndex).split(QLatin1String(", "));
+ for (int ndx = 0; ndx < childrenStrings.count(); ndx++) {
+ int childIndex = childrenStrings[ndx].toInt();
+ if (v8eventBuffer.value(childIndex)) {
+ QV8EventSub *newChild = new QV8EventSub(v8eventBuffer[childIndex]);
+ QV8EventSub *newParent = new QV8EventSub(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
+ foreach (QV8EventData *storedV8Event, v8eventBuffer.values()) {
+ storedV8Event->eventHashStr =
+ getHashStringForV8Event(
+ storedV8Event->displayName, storedV8Event->functionName);
+ d->v8EventHash[storedV8Event->eventHashStr] = storedV8Event;
+ }
+
+ d->collectV8Statistics();
+
+}
+
+void QV8ProfilerDataModel::complete()
+{
+ collectV8Statistics();
+ emit changed();
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/plugins/qmlprofiler/qv8profilerdatamodel.h b/plugins/qmlprofiler/qv8profilerdatamodel.h
new file mode 100644
index 0000000000..0dc44ba3ad
--- /dev/null
+++ b/plugins/qmlprofiler/qv8profilerdatamodel.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QV8PROFILERDATAMODEL_H
+#define QV8PROFILERDATAMODEL_H
+
+#include <QObject>
+#include <QHash>
+
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+
+namespace QmlProfiler {
+namespace Internal {
+
+struct QV8EventSub;
+
+struct QV8EventData
+{
+ QV8EventData();
+ ~QV8EventData();
+
+ QString displayName;
+ QString eventHashStr;
+ QString filename;
+ QString functionName;
+ int line;
+ double totalTime; // given in milliseconds
+ double totalPercent;
+ double selfTime;
+ double selfPercent;
+ QHash <QString, QV8EventSub *> parentHash;
+ QHash <QString, QV8EventSub *> childrenHash;
+ int eventId;
+
+ QV8EventData &operator=(const QV8EventData &ref);
+};
+
+struct QV8EventSub {
+ QV8EventSub(QV8EventData *from) : reference(from), totalTime(0) {}
+ QV8EventSub(QV8EventSub *from) : reference(from->reference), totalTime(from->totalTime) {}
+
+ QV8EventData *reference;
+ qint64 totalTime;
+};
+
+class QV8ProfilerDataModel : public QObject
+{
+ Q_OBJECT
+public:
+ QV8ProfilerDataModel(QObject *parent = 0);
+ ~QV8ProfilerDataModel();
+
+ void clear();
+ bool isEmpty() const;
+ QList<QV8EventData *> getV8Events() const;
+ QV8EventData *v8EventDescription(int eventId) const;
+
+ qint64 v8MeasuredTime() const;
+ void collectV8Statistics();
+
+ void save(QXmlStreamWriter &stream);
+ void load(QXmlStreamReader &stream);
+
+ void complete();
+
+signals:
+ void changed();
+
+public slots:
+ void addV8Event(int depth,
+ const QString &function,
+ const QString &filename,
+ int lineNumber,
+ double totalTime,
+ double selfTime);
+
+private:
+ class QV8ProfilerDataModelPrivate;
+ QV8ProfilerDataModelPrivate *d;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QV8PROFILERDATAMODEL_H
diff --git a/plugins/qmlprofiler/qv8profilereventview.cpp b/plugins/qmlprofiler/qv8profilereventview.cpp
new file mode 100644
index 0000000000..e50c533e02
--- /dev/null
+++ b/plugins/qmlprofiler/qv8profilereventview.cpp
@@ -0,0 +1,742 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qv8profilereventview.h"
+
+#include <QUrl>
+#include <QHash>
+
+#include <QStandardItem>
+#include <QHeaderView>
+
+#include <QApplication>
+#include <QClipboard>
+
+#include <QContextMenuEvent>
+#include <QDebug>
+
+#include <coreplugin/minisplitter.h>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+
+#include "qmlprofilerviewmanager.h"
+#include "qmlprofilertool.h"
+#include "qv8profilerdatamodel.h"
+#include <QMenu>
+
+#include <utils/qtcassert.h>
+
+using namespace QmlDebug;
+
+namespace QmlProfiler {
+namespace Internal {
+
+enum ItemRole {
+ EventHashStrRole = Qt::UserRole+1,
+ FilenameRole = Qt::UserRole+2,
+ LineRole = Qt::UserRole+3,
+ ColumnRole = Qt::UserRole+4,
+ EventIdRole = Qt::UserRole+5
+};
+
+////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class EventsViewItem : public QStandardItem
+{
+public:
+ EventsViewItem(const QString &text) : QStandardItem(text) {}
+
+ virtual bool operator<(const QStandardItem &other) const
+ {
+ if (data().type() == QVariant::String) {
+ // first column
+ if (column() == 0) {
+ return data(FilenameRole).toString() == other.data(FilenameRole).toString() ?
+ data(LineRole).toInt() < other.data(LineRole).toInt() :
+ data(FilenameRole).toString() < other.data(FilenameRole).toString();
+ } else {
+ return data().toString().toLower() < other.data().toString().toLower();
+ }
+ }
+
+ return data().toDouble() < other.data().toDouble();
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class QV8ProfilerEventsWidget::QV8ProfilerEventsWidgetPrivate
+{
+public:
+ QV8ProfilerEventsWidgetPrivate(QV8ProfilerEventsWidget *qq):q(qq) {}
+ ~QV8ProfilerEventsWidgetPrivate() {}
+
+ QV8ProfilerEventsWidget *q;
+
+ Analyzer::IAnalyzerTool *m_profilerTool;
+ QmlProfilerViewManager *m_viewContainer;
+
+ QV8ProfilerEventsMainView *m_eventTree;
+ QV8ProfilerEventRelativesView *m_eventChildren;
+ QV8ProfilerEventRelativesView *m_eventParents;
+
+ QV8ProfilerDataModel *v8Model;
+};
+
+QV8ProfilerEventsWidget::QV8ProfilerEventsWidget(QWidget *parent,
+ Analyzer::IAnalyzerTool *profilerTool,
+ QmlProfilerViewManager *container,
+ QmlProfilerModelManager *profilerModelManager )
+ : QWidget(parent), d(new QV8ProfilerEventsWidgetPrivate(this))
+{
+ setObjectName(QLatin1String("QmlProfilerV8ProfileView"));
+
+ d->v8Model = profilerModelManager->v8Model();
+
+ d->m_eventTree = new QV8ProfilerEventsMainView(this, d->v8Model);
+ connect(d->m_eventTree, SIGNAL(gotoSourceLocation(QString,int,int)), this, SIGNAL(gotoSourceLocation(QString,int,int)));
+
+ d->m_eventChildren = new QV8ProfilerEventRelativesView(d->v8Model,
+ QV8ProfilerEventRelativesView::ChildrenView,
+ this);
+ d->m_eventParents = new QV8ProfilerEventRelativesView(d->v8Model,
+ QV8ProfilerEventRelativesView::ParentsView,
+ this);
+ connect(d->m_eventTree, SIGNAL(eventSelected(int)), d->m_eventChildren, SLOT(displayEvent(int)));
+ connect(d->m_eventTree, SIGNAL(eventSelected(int)), d->m_eventParents, SLOT(displayEvent(int)));
+ connect(d->m_eventChildren, SIGNAL(eventClicked(int)), d->m_eventTree, SLOT(selectEvent(int)));
+ connect(d->m_eventParents, SIGNAL(eventClicked(int)), d->m_eventTree, SLOT(selectEvent(int)));
+
+ // widget arrangement
+ QVBoxLayout *groupLayout = new QVBoxLayout;
+ groupLayout->setContentsMargins(0,0,0,0);
+ groupLayout->setSpacing(0);
+
+ Core::MiniSplitter *splitterVertical = new Core::MiniSplitter;
+ splitterVertical->addWidget(d->m_eventTree);
+ Core::MiniSplitter *splitterHorizontal = new Core::MiniSplitter;
+ splitterHorizontal->addWidget(d->m_eventParents);
+ splitterHorizontal->addWidget(d->m_eventChildren);
+ splitterHorizontal->setOrientation(Qt::Horizontal);
+ splitterVertical->addWidget(splitterHorizontal);
+ splitterVertical->setOrientation(Qt::Vertical);
+ splitterVertical->setStretchFactor(0,5);
+ splitterVertical->setStretchFactor(1,2);
+ groupLayout->addWidget(splitterVertical);
+ setLayout(groupLayout);
+
+ d->m_profilerTool = profilerTool;
+ d->m_viewContainer = container;
+
+}
+
+QV8ProfilerEventsWidget::~QV8ProfilerEventsWidget()
+{
+ delete d;
+}
+
+void QV8ProfilerEventsWidget::clear()
+{
+ d->m_eventTree->clear();
+ d->m_eventChildren->clear();
+ d->m_eventParents->clear();
+}
+
+QModelIndex QV8ProfilerEventsWidget::selectedItem() const
+{
+ return d->m_eventTree->selectedItem();
+}
+
+void QV8ProfilerEventsWidget::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QTC_ASSERT(d->m_viewContainer, return;);
+
+ QMenu menu;
+ QAction *copyRowAction = 0;
+ QAction *copyTableAction = 0;
+
+ QmlProfilerTool *profilerTool = qobject_cast<QmlProfilerTool *>(d->m_profilerTool);
+ QPoint position = ev->globalPos();
+
+ if (profilerTool) {
+ QList <QAction *> commonActions = profilerTool->profilerContextMenuActions();
+ foreach (QAction *act, commonActions) {
+ menu.addAction(act);
+ }
+ }
+
+ if (mouseOnTable(position)) {
+ menu.addSeparator();
+ if (selectedItem().isValid())
+ copyRowAction = menu.addAction(tr("Copy Row"));
+ copyTableAction = menu.addAction(tr("Copy Table"));
+ }
+
+ QAction *selectedAction = menu.exec(position);
+
+ if (selectedAction) {
+ if (selectedAction == copyRowAction)
+ copyRowToClipboard();
+ if (selectedAction == copyTableAction)
+ copyTableToClipboard();
+ }
+}
+
+void QV8ProfilerEventsWidget::resizeEvent(QResizeEvent *event)
+{
+ QWidget::resizeEvent(event);
+ emit resized();
+}
+
+bool QV8ProfilerEventsWidget::mouseOnTable(const QPoint &position) const
+{
+ QPoint tableTopLeft = d->m_eventTree->mapToGlobal(QPoint(0,0));
+ QPoint tableBottomRight = d->m_eventTree->mapToGlobal(QPoint(d->m_eventTree->width(), d->m_eventTree->height()));
+ return (position.x() >= tableTopLeft.x() && position.x() <= tableBottomRight.x() && position.y() >= tableTopLeft.y() && position.y() <= tableBottomRight.y());
+}
+
+void QV8ProfilerEventsWidget::copyTableToClipboard() const
+{
+ d->m_eventTree->copyTableToClipboard();
+}
+
+void QV8ProfilerEventsWidget::copyRowToClipboard() const
+{
+ d->m_eventTree->copyRowToClipboard();
+}
+
+void QV8ProfilerEventsWidget::updateSelectedEvent(int eventId) const
+{
+ if (d->m_eventTree->selectedEventId() != eventId)
+ d->m_eventTree->selectEvent(eventId);
+}
+
+void QV8ProfilerEventsWidget::selectBySourceLocation(const QString &filename, int line, int column)
+{
+ // This slot is used to connect the javascript pane with the qml events pane
+ // Our javascript trace data does not store column information
+ // thus we ignore it here
+ Q_UNUSED(column);
+ d->m_eventTree->selectEventByLocation(filename, line);
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class QV8ProfilerEventsMainView::QV8ProfilerEventsMainViewPrivate
+{
+public:
+ QV8ProfilerEventsMainViewPrivate(QV8ProfilerEventsMainView *qq) : q(qq) {}
+
+ void buildV8ModelFromList( const QList<QV8EventData *> &list );
+ int getFieldCount();
+
+ QString textForItem(QStandardItem *item, bool recursive) const;
+
+
+ QV8ProfilerEventsMainView *q;
+
+ QV8ProfilerDataModel *m_v8Model;
+ QStandardItemModel *m_model;
+ QList<bool> m_fieldShown;
+ QHash<int, int> m_columnIndex; // maps field enum to column index
+ int m_firstNumericColumn;
+ bool m_preventSelectBounce;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+QV8ProfilerEventsMainView::QV8ProfilerEventsMainView(QWidget *parent,
+ QV8ProfilerDataModel *v8Model)
+: QmlProfilerTreeView(parent), d(new QV8ProfilerEventsMainViewPrivate(this))
+{
+ setObjectName(QLatin1String("QmlProfilerEventsTable"));
+ setSortingEnabled(false);
+
+ d->m_model = new QStandardItemModel(this);
+ setModel(d->m_model);
+ connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
+
+ d->m_v8Model = v8Model;
+ connect(d->m_v8Model, SIGNAL(changed()), this, SLOT(buildModel()));
+ d->m_firstNumericColumn = 0;
+ d->m_preventSelectBounce = false;
+
+ setFieldViewable(Name, true);
+ setFieldViewable(Type, false);
+ setFieldViewable(Percent, true);
+ setFieldViewable(TotalDuration, true);
+ setFieldViewable(SelfPercent, true);
+ setFieldViewable(SelfDuration, true);
+ setFieldViewable(CallCount, false);
+ setFieldViewable(TimePerCall, false);
+ setFieldViewable(MaxTime, false);
+ setFieldViewable(MinTime, false);
+ setFieldViewable(MedianTime, false);
+ setFieldViewable(Details, true);
+
+ buildModel();
+}
+
+QV8ProfilerEventsMainView::~QV8ProfilerEventsMainView()
+{
+ clear();
+ delete d->m_model;
+ delete d;
+}
+
+void QV8ProfilerEventsMainView::setFieldViewable(Fields field, bool show)
+{
+ if (field < MaxFields) {
+ int length = d->m_fieldShown.count();
+ if (field >= length) {
+ for (int i=length; i<MaxFields; i++)
+ d->m_fieldShown << false;
+ }
+ d->m_fieldShown[field] = show;
+ }
+}
+
+
+void QV8ProfilerEventsMainView::setHeaderLabels()
+{
+ int fieldIndex = 0;
+ d->m_firstNumericColumn = 0;
+
+ d->m_columnIndex.clear();
+ if (d->m_fieldShown[Name]) {
+ d->m_columnIndex[Name] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Location")));
+ d->m_firstNumericColumn++;
+ }
+ if (d->m_fieldShown[Type]) {
+ d->m_columnIndex[Type] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Type")));
+ d->m_firstNumericColumn++;
+ }
+ if (d->m_fieldShown[Percent]) {
+ d->m_columnIndex[Percent] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Time in Percent")));
+ }
+ if (d->m_fieldShown[TotalDuration]) {
+ d->m_columnIndex[TotalDuration] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Total Time")));
+ }
+ if (d->m_fieldShown[SelfPercent]) {
+ d->m_columnIndex[Type] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Self Time in Percent")));
+ }
+ if (d->m_fieldShown[SelfDuration]) {
+ d->m_columnIndex[SelfDuration] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Self Time")));
+ }
+ if (d->m_fieldShown[CallCount]) {
+ d->m_columnIndex[CallCount] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Calls")));
+ }
+ if (d->m_fieldShown[TimePerCall]) {
+ d->m_columnIndex[TimePerCall] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Mean Time")));
+ }
+ if (d->m_fieldShown[MedianTime]) {
+ d->m_columnIndex[MedianTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Median Time")));
+ }
+ if (d->m_fieldShown[MaxTime]) {
+ d->m_columnIndex[MaxTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Longest Time")));
+ }
+ if (d->m_fieldShown[MinTime]) {
+ d->m_columnIndex[MinTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Shortest Time")));
+ }
+ if (d->m_fieldShown[Details]) {
+ d->m_columnIndex[Details] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Details")));
+ }
+}
+
+void QV8ProfilerEventsMainView::clear()
+{
+ d->m_model->clear();
+ d->m_model->setColumnCount(d->getFieldCount());
+
+ setHeaderLabels();
+ setSortingEnabled(false);
+}
+
+int QV8ProfilerEventsMainView::QV8ProfilerEventsMainViewPrivate::getFieldCount()
+{
+ int count = 0;
+ for (int i=0; i < m_fieldShown.count(); ++i)
+ if (m_fieldShown[i])
+ count++;
+ return count;
+}
+
+void QV8ProfilerEventsMainView::buildModel()
+{
+ clear();
+ d->buildV8ModelFromList( d->m_v8Model->getV8Events() );
+
+ setRootIsDecorated(false);
+ setSortingEnabled(true);
+ sortByColumn(d->m_firstNumericColumn,Qt::DescendingOrder);
+
+ expandAll();
+ if (d->m_fieldShown[Name])
+ resizeColumnToContents(0);
+
+ if (d->m_fieldShown[Type])
+ resizeColumnToContents(d->m_fieldShown[Name]?1:0);
+ collapseAll();
+}
+
+void QV8ProfilerEventsMainView::QV8ProfilerEventsMainViewPrivate::buildV8ModelFromList(const QList<QV8EventData *> &list)
+{
+ for (int index = 0; index < list.count(); index++) {
+ QV8EventData *v8event = list.at(index);
+ QList<QStandardItem *> newRow;
+
+ if (m_fieldShown[Name])
+ newRow << new EventsViewItem(v8event->displayName);
+
+ if (m_fieldShown[Percent]) {
+ newRow << new EventsViewItem(QString::number(v8event->totalPercent,'f',2)+QLatin1String(" %"));
+ newRow.last()->setData(QVariant(v8event->totalPercent));
+ }
+
+ if (m_fieldShown[TotalDuration]) {
+ newRow << new EventsViewItem(displayTime(v8event->totalTime));
+ newRow.last()->setData(QVariant(v8event->totalTime));
+ }
+
+ if (m_fieldShown[SelfPercent]) {
+ newRow << new EventsViewItem(QString::number(v8event->selfPercent,'f',2)+QLatin1String(" %"));
+ newRow.last()->setData(QVariant(v8event->selfPercent));
+ }
+
+ if (m_fieldShown[SelfDuration]) {
+ newRow << new EventsViewItem(displayTime(v8event->selfTime));
+ newRow.last()->setData(QVariant(v8event->selfTime));
+ }
+
+ if (m_fieldShown[Details]) {
+ newRow << new EventsViewItem(v8event->functionName);
+ newRow.last()->setData(QVariant(v8event->functionName));
+ }
+
+ if (!newRow.isEmpty()) {
+ // no edit
+ foreach (QStandardItem *item, newRow)
+ item->setEditable(false);
+
+ // metadata
+ newRow.at(0)->setData(QString::fromLatin1("%1:%2").arg(v8event->filename, QString::number(v8event->line)), EventHashStrRole);
+ newRow.at(0)->setData(QVariant(v8event->filename), FilenameRole);
+ newRow.at(0)->setData(QVariant(v8event->line), LineRole);
+ newRow.at(0)->setData(QVariant(0),ColumnRole); // v8 events have no column info
+ newRow.at(0)->setData(QVariant(v8event->eventId), EventIdRole);
+
+ // append
+ m_model->invisibleRootItem()->appendRow(newRow);
+ }
+ }
+}
+
+QString QV8ProfilerEventsMainView::displayTime(double time)
+{
+ if (time < 1e6)
+ return QString::number(time/1e3,'f',3) + trUtf8(" \xc2\xb5s");
+ if (time < 1e9)
+ return QString::number(time/1e6,'f',3) + tr(" ms");
+
+ return QString::number(time/1e9,'f',3) + tr(" s");
+}
+
+QString QV8ProfilerEventsMainView::nameForType(int typeNumber)
+{
+ switch (typeNumber) {
+ case 0: return QV8ProfilerEventsMainView::tr("Paint");
+ case 1: return QV8ProfilerEventsMainView::tr("Compile");
+ case 2: return QV8ProfilerEventsMainView::tr("Create");
+ case 3: return QV8ProfilerEventsMainView::tr("Binding");
+ case 4: return QV8ProfilerEventsMainView::tr("Signal");
+ }
+ return QString();
+}
+
+int QV8ProfilerEventsMainView::selectedEventId() const
+{
+ QModelIndex index = selectedItem();
+ if (!index.isValid())
+ return -1;
+ QStandardItem *item = d->m_model->item(index.row(), 0);
+ return item->data(EventIdRole).toInt();
+}
+
+void QV8ProfilerEventsMainView::jumpToItem(const QModelIndex &index)
+{
+ if (d->m_preventSelectBounce)
+ return;
+
+ d->m_preventSelectBounce = true;
+ QStandardItem *clickedItem = d->m_model->itemFromIndex(index);
+ QStandardItem *infoItem;
+ if (clickedItem->parent())
+ infoItem = clickedItem->parent()->child(clickedItem->row(), 0);
+ else
+ infoItem = d->m_model->item(index.row(), 0);
+
+ // show in editor
+ int line = infoItem->data(LineRole).toInt();
+ int column = infoItem->data(ColumnRole).toInt();
+ QString fileName = infoItem->data(FilenameRole).toString();
+ if (line!=-1 && !fileName.isEmpty())
+ emit gotoSourceLocation(fileName, line, column);
+
+ // show in callers/callees subwindow
+ emit eventSelected(infoItem->data(EventIdRole).toInt());
+
+ // show in timelinerenderer
+ emit showEventInTimeline(infoItem->data(EventIdRole).toInt());
+
+ d->m_preventSelectBounce = false;
+}
+
+void QV8ProfilerEventsMainView::selectEvent(int eventId)
+{
+ for (int i=0; i<d->m_model->rowCount(); i++) {
+ QStandardItem *infoItem = d->m_model->item(i, 0);
+ if (infoItem->data(EventIdRole).toInt() == eventId) {
+ setCurrentIndex(d->m_model->indexFromItem(infoItem));
+ jumpToItem(currentIndex());
+ return;
+ }
+ }
+}
+
+void QV8ProfilerEventsMainView::selectEventByLocation(const QString &filename, int line)
+{
+ if (d->m_preventSelectBounce)
+ return;
+
+ for (int i=0; i<d->m_model->rowCount(); i++) {
+ QStandardItem *infoItem = d->m_model->item(i, 0);
+ if (currentIndex() != d->m_model->indexFromItem(infoItem) && infoItem->data(FilenameRole).toString() == filename && infoItem->data(LineRole).toInt() == line) {
+ setCurrentIndex(d->m_model->indexFromItem(infoItem));
+ jumpToItem(currentIndex());
+ return;
+ }
+ }
+}
+
+QModelIndex QV8ProfilerEventsMainView::selectedItem() const
+{
+ QModelIndexList sel = selectedIndexes();
+ if (sel.isEmpty())
+ return QModelIndex();
+ else
+ return sel.first();
+}
+
+void QV8ProfilerEventsMainView::changeDetailsForEvent(int eventId, const QString &newString)
+{
+ for (int i=0; i<d->m_model->rowCount(); i++) {
+ QStandardItem *infoItem = d->m_model->item(i, 0);
+ if (infoItem->data(EventIdRole).toInt() == eventId) {
+ d->m_model->item(i,d->m_columnIndex[Details])->setData(QVariant(newString),Qt::DisplayRole);
+ d->m_model->item(i,d->m_columnIndex[Details])->setData(QVariant(newString));
+ return;
+ }
+ }
+}
+
+QString QV8ProfilerEventsMainView::QV8ProfilerEventsMainViewPrivate::textForItem(QStandardItem *item, bool recursive = true) const
+{
+ QString str;
+
+ if (recursive) {
+ // indentation
+ QStandardItem *itemParent = item->parent();
+ while (itemParent) {
+ str += QLatin1String(" ");
+ itemParent = itemParent->parent();
+ }
+ }
+
+ // item's data
+ int colCount = m_model->columnCount();
+ for (int j = 0; j < colCount; ++j) {
+ QStandardItem *colItem = item->parent() ? item->parent()->child(item->row(),j) : m_model->item(item->row(),j);
+ str += colItem->data(Qt::DisplayRole).toString();
+ if (j < colCount-1) str += QLatin1Char('\t');
+ }
+ str += QLatin1Char('\n');
+
+ // recursively print children
+ if (recursive && item->child(0))
+ for (int j = 0; j != item->rowCount(); j++)
+ str += textForItem(item->child(j));
+
+ return str;
+}
+
+void QV8ProfilerEventsMainView::copyTableToClipboard() const
+{
+ QString str;
+ // headers
+ int columnCount = d->m_model->columnCount();
+ for (int i = 0; i < columnCount; ++i) {
+ str += d->m_model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
+ if (i < columnCount - 1)
+ str += QLatin1Char('\t');
+ else
+ str += QLatin1Char('\n');
+ }
+ // data
+ int rowCount = d->m_model->rowCount();
+ for (int i = 0; i != rowCount; ++i) {
+ str += d->textForItem(d->m_model->item(i));
+ }
+ QClipboard *clipboard = QApplication::clipboard();
+ clipboard->setText(str, QClipboard::Selection);
+ clipboard->setText(str, QClipboard::Clipboard);
+}
+
+void QV8ProfilerEventsMainView::copyRowToClipboard() const
+{
+ QString str;
+ str = d->textForItem(d->m_model->itemFromIndex(selectedItem()), false);
+
+ QClipboard *clipboard = QApplication::clipboard();
+ clipboard->setText(str, QClipboard::Selection);
+ clipboard->setText(str, QClipboard::Clipboard);
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+QV8ProfilerEventRelativesView::QV8ProfilerEventRelativesView(QV8ProfilerDataModel *model,
+ SubViewType viewType,
+ QWidget *parent)
+ : QmlProfilerTreeView(parent)
+ , m_type(viewType)
+ , m_v8Model(model)
+ , m_model(new QStandardItemModel(this))
+{
+ setModel(m_model);
+
+ updateHeader();
+ setSortingEnabled(false);
+
+ connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(jumpToItem(QModelIndex)));
+}
+
+QV8ProfilerEventRelativesView::~QV8ProfilerEventRelativesView()
+{
+}
+
+void QV8ProfilerEventRelativesView::displayEvent(int index)
+{
+ QV8EventData *event = m_v8Model->v8EventDescription(index);
+ QTC_CHECK(event);
+
+ QList<QV8EventSub*> events;
+ if (m_type == ParentsView)
+ events = event->parentHash.values();
+ else
+ events = event->childrenHash.values();
+
+ rebuildTree(events);
+
+ updateHeader();
+ resizeColumnToContents(0);
+ setSortingEnabled(true);
+ sortByColumn(1);
+}
+
+void QV8ProfilerEventRelativesView::rebuildTree(QList<QV8EventSub*> events)
+{
+ clear();
+
+ QStandardItem *topLevelItem = m_model->invisibleRootItem();
+
+ foreach (QV8EventSub *event, events) {
+ QList<QStandardItem *> newRow;
+ newRow << new EventsViewItem(event->reference->displayName);
+ newRow << new EventsViewItem(QV8ProfilerEventsMainView::displayTime(event->totalTime));
+ newRow << new EventsViewItem(event->reference->functionName);
+ newRow.at(0)->setData(QVariant(event->reference->eventId), EventIdRole);
+ newRow.at(1)->setData(QVariant(event->totalTime));
+
+ foreach (QStandardItem *item, newRow)
+ item->setEditable(false);
+
+ topLevelItem->appendRow(newRow);
+ }
+}
+
+void QV8ProfilerEventRelativesView::clear()
+{
+ m_model->clear();
+}
+
+void QV8ProfilerEventRelativesView::updateHeader()
+{
+ m_model->setColumnCount(3);
+
+ int columnIndex = 0;
+ if (m_type == ChildrenView)
+ m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee")));
+ else
+ m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller")));
+
+ m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Type")));
+
+ m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Total Time")));
+
+ m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Calls")));
+
+ if (m_type == ChildrenView)
+ m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee Description")));
+ else
+ m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller Description")));
+}
+
+void QV8ProfilerEventRelativesView::jumpToItem(const QModelIndex &index)
+{
+ QStandardItem *infoItem = m_model->item(index.row(), 0);
+ emit eventClicked(infoItem->data(EventIdRole).toInt());
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/plugins/qmlprofiler/qv8profilereventview.h b/plugins/qmlprofiler/qv8profilereventview.h
new file mode 100644
index 0000000000..25db4efed4
--- /dev/null
+++ b/plugins/qmlprofiler/qv8profilereventview.h
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QV8PROFILEREVENTVIEW_H
+#define QV8PROFILEREVENTVIEW_H
+
+#include <QStandardItemModel>
+#include <qmldebug/qmlprofilereventtypes.h>
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilereventsmodelproxy.h"
+#include "qmlprofilertreeview.h"
+
+#include <analyzerbase/ianalyzertool.h>
+
+#include "qmlprofilerviewmanager.h"
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QV8ProfilerEventsMainView;
+class QV8ProfilerEventRelativesView;
+struct QV8EventSub;
+
+
+class QV8ProfilerEventsWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit QV8ProfilerEventsWidget(QWidget *parent,
+ Analyzer::IAnalyzerTool *profilerTool,
+ QmlProfilerViewManager *container,
+ QmlProfilerModelManager *profilerModelManager );
+ ~QV8ProfilerEventsWidget();
+
+ void clear();
+
+ void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
+ QModelIndex selectedItem() const;
+ bool mouseOnTable(const QPoint &position) const;
+ void copyTableToClipboard() const;
+ void copyRowToClipboard() const;
+
+signals:
+ void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
+ void showEventInTimeline(int eventId);
+ void resized();
+
+public slots:
+ void updateSelectedEvent(int eventId) const;
+ void selectBySourceLocation(const QString &filename, int line, int column);
+
+protected:
+ void contextMenuEvent(QContextMenuEvent *ev);
+ virtual void resizeEvent(QResizeEvent *event);
+
+private:
+ class QV8ProfilerEventsWidgetPrivate;
+ QV8ProfilerEventsWidgetPrivate *d;
+};
+
+class QV8ProfilerEventsMainView : public QmlProfilerTreeView
+{
+ Q_OBJECT
+public:
+ enum Fields {
+ Name,
+ Type,
+ Percent,
+ TotalDuration,
+ SelfPercent,
+ SelfDuration,
+ CallCount,
+ TimePerCall,
+ MaxTime,
+ MinTime,
+ MedianTime,
+ Details,
+
+ MaxFields
+ };
+
+ explicit QV8ProfilerEventsMainView(QWidget *parent,
+ QV8ProfilerDataModel *v8Model);
+ ~QV8ProfilerEventsMainView();
+
+ void setFieldViewable(Fields field, bool show);
+ void setShowAnonymousEvents( bool showThem );
+
+ QModelIndex selectedItem() const;
+ void copyTableToClipboard() const;
+ void copyRowToClipboard() const;
+
+ static QString displayTime(double time);
+ static QString nameForType(int typeNumber);
+
+ int selectedEventId() const;
+
+ void setShowExtendedStatistics(bool);
+ bool showExtendedStatistics() const;
+
+signals:
+ void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
+ void eventSelected(int eventId);
+ void showEventInTimeline(int eventId);
+
+public slots:
+ void clear();
+ void jumpToItem(const QModelIndex &index);
+ void selectEvent(int eventId);
+ void selectEventByLocation(const QString &filename, int line);
+ void buildModel();
+ void changeDetailsForEvent(int eventId, const QString &newString);
+
+private:
+ void setHeaderLabels();
+
+private:
+ class QV8ProfilerEventsMainViewPrivate;
+ QV8ProfilerEventsMainViewPrivate *d;
+};
+
+class QV8ProfilerEventRelativesView : public QmlProfilerTreeView
+{
+ Q_OBJECT
+public:
+ enum SubViewType {
+ ParentsView,
+ ChildrenView
+ };
+
+ QV8ProfilerEventRelativesView(QV8ProfilerDataModel *model, SubViewType viewType,
+ QWidget *parent);
+ ~QV8ProfilerEventRelativesView();
+
+signals:
+ void eventClicked(int eventId);
+
+public slots:
+ void displayEvent(int eventId);
+ void jumpToItem(const QModelIndex &);
+ void clear();
+
+private:
+ void rebuildTree(QList<QV8EventSub*> events);
+ void updateHeader();
+
+ QV8ProfilerEventRelativesView::SubViewType m_type;
+ QV8ProfilerDataModel *m_v8Model;
+ QStandardItemModel *m_model;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QV8PROFILEREVENTVIEW_H
diff --git a/plugins/qmlprofiler/timelinerenderer.cpp b/plugins/qmlprofiler/timelinerenderer.cpp
new file mode 100644
index 0000000000..c9cb5d2a97
--- /dev/null
+++ b/plugins/qmlprofiler/timelinerenderer.cpp
@@ -0,0 +1,489 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "timelinerenderer.h"
+
+#include <qdeclarativecontext.h>
+#include <qdeclarativeproperty.h>
+#include <QTimer>
+#include <QPixmap>
+#include <QPainter>
+#include <QGraphicsSceneMouseEvent>
+
+#include <math.h>
+
+using namespace QmlProfiler::Internal;
+
+const int DefaultRowHeight = 30;
+
+TimelineRenderer::TimelineRenderer(QDeclarativeItem *parent) :
+ QDeclarativeItem(parent), m_startTime(0), m_endTime(0), m_spacing(0),
+ m_lastStartTime(0), m_lastEndTime(0)
+ , m_profilerModelProxy(0)
+{
+ clearData();
+ setFlag(QGraphicsItem::ItemHasNoContents, false);
+ setAcceptedMouseButtons(Qt::LeftButton);
+ setAcceptHoverEvents(true);
+ for (int i=0; i<QmlDebug::MaximumQmlEventType; i++)
+ m_rowsExpanded << false;
+}
+
+void TimelineRenderer::componentComplete()
+{
+ const QMetaObject *metaObject = this->metaObject();
+ int propertyCount = metaObject->propertyCount();
+ int requestPaintMethod = metaObject->indexOfMethod("requestPaint()");
+ for (int ii = TimelineRenderer::staticMetaObject.propertyCount(); ii < propertyCount; ++ii) {
+ QMetaProperty p = metaObject->property(ii);
+ if (p.hasNotifySignal())
+ QMetaObject::connect(this, p.notifySignalIndex(), this, requestPaintMethod, 0, 0);
+ }
+ QDeclarativeItem::componentComplete();
+}
+
+void TimelineRenderer::requestPaint()
+{
+ update();
+}
+
+void TimelineRenderer::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *)
+{
+ qint64 windowDuration = m_endTime - m_startTime;
+ if (windowDuration <= 0)
+ return;
+
+ m_spacing = qreal(width()) / windowDuration;
+
+ m_rowWidths.clear();
+ // The "1+" is because the reference screenshot features an empty row per type, in order to leave space for the title
+ for (int i=0; i<QmlDebug::MaximumQmlEventType; i++) {
+ m_rowWidths << m_profilerModelProxy->categoryDepth(i);
+ }
+
+ // event rows
+ m_rowStarts.clear();
+ int pos = 0;
+ for (int i=0; i<QmlDebug::MaximumQmlEventType; i++) {
+ m_rowStarts << pos;
+ pos += DefaultRowHeight * m_rowWidths[i];
+ }
+
+ p->setPen(Qt::transparent);
+
+ // speedup: don't draw overlapping events, just skip them
+ m_rowLastX.clear();
+ for (int i=0; i<QmlDebug::MaximumQmlEventType; i++)
+ for (int j=0; j<m_rowWidths[i]; j++)
+ m_rowLastX << -m_startTime * m_spacing;
+
+ int firstIndex = m_profilerModelProxy->findFirstIndex(m_startTime);
+ int lastIndex = m_profilerModelProxy->findLastIndex(m_endTime);
+
+ if (lastIndex < m_profilerModelProxy->count()) {
+ drawItemsToPainter(p, firstIndex, lastIndex);
+ drawSelectionBoxes(p, firstIndex, lastIndex);
+ drawBindingLoopMarkers(p, firstIndex, lastIndex);
+ }
+ m_lastStartTime = m_startTime;
+ m_lastEndTime = m_endTime;
+
+}
+
+QColor TimelineRenderer::colorForItem(int itemIndex)
+{
+ int ndx = m_profilerModelProxy->getEventId(itemIndex);
+ return QColor::fromHsl((ndx*25)%360, 76, 166);
+}
+
+void TimelineRenderer::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex)
+{
+ int x, y, width, height, rowNumber, eventType;
+ for (int i = fromIndex; i <= toIndex; i++) {
+ x = (m_profilerModelProxy->getStartTime(i) - m_startTime) * m_spacing;
+
+ eventType = m_profilerModelProxy->getEventType(i);
+ y = m_rowStarts[eventType] + m_profilerModelProxy->getEventRow(i) * DefaultRowHeight;
+
+ width = m_profilerModelProxy->getDuration(i)*m_spacing;
+ if (width < 1)
+ width = 1;
+
+ rowNumber = y/DefaultRowHeight;
+ if (m_rowLastX[rowNumber] > x+width)
+ continue;
+ m_rowLastX[rowNumber] = x+width;
+
+ // special: animations
+ /*if (eventType == 0 && m_profilerDataModel->getAnimationCount(i) >= 0) {
+ double scale = m_profilerDataModel->getMaximumAnimationCount() -
+ m_profilerDataModel->getMinimumAnimationCount();
+ double fraction;
+ if (scale > 1)
+ fraction = (double)(m_profilerDataModel->getAnimationCount(i) -
+ m_profilerDataModel->getMinimumAnimationCount()) / scale;
+ else
+ fraction = 1.0;
+ height = DefaultRowHeight * (fraction * 0.85 + 0.15);
+ y += DefaultRowHeight - height;
+
+ double fpsFraction = m_profilerDataModel->getFramerate(i) / 60.0;
+ if (fpsFraction > 1.0)
+ fpsFraction = 1.0;
+ p->setBrush(QColor::fromHsl((fpsFraction*96)+10, 76, 166));
+ p->drawRect(x, y, width, height);
+ } else */ {
+ // normal events
+ p->setBrush(colorForItem(i));
+ p->drawRect(x, y, width, DefaultRowHeight);
+ }
+ }
+}
+
+void TimelineRenderer::drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex)
+{
+ if (m_selectedItem == -1)
+ return;
+
+ int id = m_profilerModelProxy->getEventId(m_selectedItem);
+
+ p->setBrush(Qt::transparent);
+ QColor selectionColor = Qt::blue;
+ if (m_selectionLocked)
+ selectionColor = QColor(96,0,255);
+ QPen strongPen(selectionColor, 3);
+ QPen lightPen(QBrush(selectionColor.lighter(130)), 2);
+ lightPen.setJoinStyle(Qt::MiterJoin);
+ p->setPen(lightPen);
+
+ int x, y, width, eventType;
+ p->setPen(lightPen);
+
+ QRect selectedItemRect(0,0,0,0);
+ for (int i = fromIndex; i <= toIndex; i++) {
+ if (m_profilerModelProxy->getEventId(i) != id)
+ continue;
+
+ x = (m_profilerModelProxy->getStartTime(i) - m_startTime) * m_spacing;
+ eventType = m_profilerModelProxy->getEventType(i);
+ y = m_rowStarts[eventType] + DefaultRowHeight * m_profilerModelProxy->getEventRow(i);
+
+ width = m_profilerModelProxy->getDuration(i)*m_spacing;
+ if (width<1)
+ width = 1;
+
+ if (i == m_selectedItem)
+ selectedItemRect = QRect(x, y-1, width, DefaultRowHeight+1);
+ else
+ p->drawRect(x,y,width,DefaultRowHeight);
+ }
+
+ // draw the selected item rectangle the last, so that it's overlayed
+ if (selectedItemRect.width() != 0) {
+ p->setPen(strongPen);
+ p->drawRect(selectedItemRect);
+ }
+}
+
+void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int fromIndex, int toIndex)
+{
+ int destindex;
+ int xfrom, xto;
+ int yfrom, yto;
+ int radius = DefaultRowHeight / 3;
+ QPen shadowPen = QPen(QColor("grey"),2);
+ QPen markerPen = QPen(QColor("orange"),2);
+ QBrush shadowBrush = QBrush(QColor("grey"));
+ QBrush markerBrush = QBrush(QColor("orange"));
+
+ p->save();
+ for (int i = fromIndex; i <= toIndex; i++) {
+ destindex = m_profilerModelProxy->getBindingLoopDest(i);
+ if (destindex >= 0) {
+ // from
+ xfrom = (m_profilerModelProxy->getStartTime(i) +
+ m_profilerModelProxy->getDuration(i)/2 -
+ m_startTime) * m_spacing;
+ yfrom = getYPosition(i);
+ yfrom += DefaultRowHeight / 2;
+
+ // to
+ xto = (m_profilerModelProxy->getStartTime(destindex) +
+ m_profilerModelProxy->getDuration(destindex)/2 -
+ m_startTime) * m_spacing;
+ yto = getYPosition(destindex);
+ yto += DefaultRowHeight / 2;
+
+ // radius
+ int eventWidth = m_profilerModelProxy->getDuration(i) * m_spacing;
+ radius = 5;
+ if (radius * 2 > eventWidth)
+ radius = eventWidth / 2;
+ if (radius < 2)
+ radius = 2;
+
+ // shadow
+ int shadowoffset = 2;
+ p->setPen(shadowPen);
+ p->setBrush(shadowBrush);
+ p->drawEllipse(QPoint(xfrom, yfrom + shadowoffset), radius, radius);
+ p->drawEllipse(QPoint(xto, yto + shadowoffset), radius, radius);
+ p->drawLine(QPoint(xfrom, yfrom + shadowoffset), QPoint(xto, yto + shadowoffset));
+
+
+ // marker
+ p->setPen(markerPen);
+ p->setBrush(markerBrush);
+ p->drawEllipse(QPoint(xfrom, yfrom), radius, radius);
+ p->drawEllipse(QPoint(xto, yto), radius, radius);
+ p->drawLine(QPoint(xfrom, yfrom), QPoint(xto, yto));
+ }
+ }
+ p->restore();
+}
+
+void TimelineRenderer::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ // special case: if there is a drag area below me, don't accept the
+ // events unless I'm actually clicking inside an item
+ if (m_currentSelection.eventIndex == -1 &&
+ event->pos().x()+x() >= m_startDragArea &&
+ event->pos().x()+x() <= m_endDragArea)
+ event->setAccepted(false);
+
+}
+
+void TimelineRenderer::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_UNUSED(event);
+ manageClicked();
+}
+
+void TimelineRenderer::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ event->setAccepted(false);
+}
+
+
+void TimelineRenderer::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+ manageHovered(event->pos().x(), event->pos().y());
+ if (m_currentSelection.eventIndex == -1)
+ event->setAccepted(false);
+}
+
+void TimelineRenderer::manageClicked()
+{
+ if (m_currentSelection.eventIndex != -1) {
+ if (m_currentSelection.eventIndex == m_selectedItem)
+ setSelectionLocked(!m_selectionLocked);
+ else
+ setSelectionLocked(true);
+ emit itemPressed(m_currentSelection.eventIndex);
+ } else {
+ setSelectionLocked(false);
+ }
+ setSelectedItem(m_currentSelection.eventIndex);
+}
+
+void TimelineRenderer::manageHovered(int x, int y)
+{
+ if (m_endTime - m_startTime <=0 || m_lastEndTime - m_lastStartTime <= 0)
+ return;
+
+ qint64 time = x * (m_endTime - m_startTime) / width() + m_startTime;
+ int row = y / DefaultRowHeight;
+
+ // already covered? nothing to do
+ if (m_currentSelection.eventIndex != -1 &&
+ time >= m_currentSelection.startTime &&
+ time <= m_currentSelection.endTime &&
+ row == m_currentSelection.row) {
+ return;
+ }
+
+ // find if there's items in the time range
+ int eventFrom = m_profilerModelProxy->findFirstIndex(time);
+ int eventTo = m_profilerModelProxy->findLastIndex(time);
+ if (eventTo < eventFrom || eventTo >= m_profilerModelProxy->count()) {
+ m_currentSelection.eventIndex = -1;
+ return;
+ }
+
+ // find if we are in the right column
+ int itemRow, eventType;
+ for (int i=eventTo; i>=eventFrom; --i) {
+ if (ceil(m_profilerModelProxy->getEndTime(i)*m_spacing) < floor(time*m_spacing))
+ continue;
+
+ eventType = m_profilerModelProxy->getEventType(i);
+ itemRow = m_rowStarts[eventType]/DefaultRowHeight +
+ m_profilerModelProxy->getEventRow(i);
+ if (itemRow == row) {
+ // match
+ m_currentSelection.eventIndex = i;
+ m_currentSelection.startTime = m_profilerModelProxy->getStartTime(i);
+ m_currentSelection.endTime = m_profilerModelProxy->getEndTime(i);
+ m_currentSelection.row = row;
+ if (!m_selectionLocked)
+ setSelectedItem(i);
+ return;
+ }
+ }
+
+ m_currentSelection.eventIndex = -1;
+ return;
+}
+
+void TimelineRenderer::clearData()
+{
+ m_startTime = 0;
+ m_endTime = 0;
+ m_lastStartTime = 0;
+ m_lastEndTime = 0;
+ m_currentSelection.startTime = -1;
+ m_currentSelection.endTime = -1;
+ m_currentSelection.row = -1;
+ m_currentSelection.eventIndex = -1;
+ m_selectedItem = -1;
+ m_selectionLocked = true;
+}
+
+qint64 TimelineRenderer::getDuration(int index) const
+{
+}
+
+QString TimelineRenderer::getFilename(int index) const
+{
+}
+
+int TimelineRenderer::getLine(int index) const
+{
+}
+
+QString TimelineRenderer::getDetails(int index) const
+{
+}
+
+int TimelineRenderer::getYPosition(int index) const
+{
+ Q_ASSERT(m_profilerModelProxy);
+ if (index >= m_profilerModelProxy->count() || m_rowStarts.isEmpty())
+ return 0;
+ int eventType = m_profilerModelProxy->getEventType(index);
+ int y = m_rowStarts[eventType] + DefaultRowHeight * m_profilerModelProxy->getEventRow(index);
+ return y;
+}
+
+void TimelineRenderer::setRowExpanded(int rowIndex, bool expanded)
+{
+ // todo: m_rowsExpanded, should that be removed? where do I have it duplicated?
+ m_rowsExpanded[rowIndex] = expanded;
+ update();
+}
+
+void TimelineRenderer::selectNext()
+{
+ if (m_profilerModelProxy->count() == 0)
+ return;
+
+ // select next in view or after
+ int newIndex = m_selectedItem+1;
+ if (newIndex >= m_profilerModelProxy->count())
+ newIndex = 0;
+ if (m_profilerModelProxy->getEndTime(newIndex) < m_startTime)
+ newIndex = m_profilerModelProxy->findFirstIndexNoParents(m_startTime);
+ setSelectedItem(newIndex);
+}
+
+void TimelineRenderer::selectPrev()
+{
+ if (m_profilerModelProxy->count() == 0)
+ return;
+
+ // select last in view or before
+ int newIndex = m_selectedItem-1;
+ if (newIndex < 0)
+ newIndex = m_profilerModelProxy->count()-1;
+ if (m_profilerModelProxy->getStartTime(newIndex) > m_endTime)
+ newIndex = m_profilerModelProxy->findLastIndex(m_endTime);
+ setSelectedItem(newIndex);
+}
+
+int TimelineRenderer::nextItemFromId(int eventId) const
+{
+ int ndx = -1;
+ if (m_selectedItem == -1)
+ ndx = m_profilerModelProxy->findFirstIndexNoParents(m_startTime);
+ else
+ ndx = m_selectedItem + 1;
+ if (ndx >= m_profilerModelProxy->count())
+ ndx = 0;
+ int startIndex = ndx;
+ do {
+ if (m_profilerModelProxy->getEventId(ndx) == eventId)
+ return ndx;
+ ndx = (ndx + 1) % m_profilerModelProxy->count();
+ } while (ndx != startIndex);
+ return -1;
+}
+
+int TimelineRenderer::prevItemFromId(int eventId) const
+{
+ int ndx = -1;
+ if (m_selectedItem == -1)
+ ndx = m_profilerModelProxy->findFirstIndexNoParents(m_startTime);
+ else
+ ndx = m_selectedItem - 1;
+ if (ndx < 0)
+ ndx = m_profilerModelProxy->count() - 1;
+ int startIndex = ndx;
+ do {
+ if (m_profilerModelProxy->getEventId(ndx) == eventId)
+ return ndx;
+ if (--ndx < 0)
+ ndx = m_profilerModelProxy->count()-1;
+ } while (ndx != startIndex);
+ return -1;
+}
+
+void TimelineRenderer::selectNextFromId(int eventId)
+{
+ int eventIndex = nextItemFromId(eventId);
+ if (eventIndex != -1)
+ setSelectedItem(eventIndex);
+}
+
+void TimelineRenderer::selectPrevFromId(int eventId)
+{
+ int eventIndex = prevItemFromId(eventId);
+ if (eventIndex != -1)
+ setSelectedItem(eventIndex);
+}
diff --git a/plugins/qmlprofiler/timelinerenderer.h b/plugins/qmlprofiler/timelinerenderer.h
new file mode 100644
index 0000000000..37b4696369
--- /dev/null
+++ b/plugins/qmlprofiler/timelinerenderer.h
@@ -0,0 +1,227 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef TIMELINERENDERER_H
+#define TIMELINERENDERER_H
+
+#include <QDeclarativeItem>
+#include <QScriptValue>
+#include "qmlprofilertimelinemodelproxy.h"
+
+namespace QmlProfiler {
+namespace Internal {
+
+class TimelineRenderer : public QDeclarativeItem
+{
+ Q_OBJECT
+ Q_PROPERTY(qint64 startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
+ Q_PROPERTY(qint64 endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
+ Q_PROPERTY(QObject *profilerModelProxy READ profilerModelProxy WRITE setProfilerModelProxy NOTIFY profilerModelProxyChanged)
+ Q_PROPERTY(bool selectionLocked READ selectionLocked WRITE setSelectionLocked NOTIFY selectionLockedChanged)
+ Q_PROPERTY(int selectedItem READ selectedItem WRITE setSelectedItem NOTIFY selectedItemChanged)
+ Q_PROPERTY(int startDragArea READ startDragArea WRITE setStartDragArea NOTIFY startDragAreaChanged)
+ Q_PROPERTY(int endDragArea READ endDragArea WRITE setEndDragArea NOTIFY endDragAreaChanged)
+
+public:
+ explicit TimelineRenderer(QDeclarativeItem *parent = 0);
+
+ qint64 startTime() const
+ {
+ return m_startTime;
+ }
+
+ qint64 endTime() const
+ {
+ return m_endTime;
+ }
+
+ bool selectionLocked() const
+ {
+ return m_selectionLocked;
+ }
+
+ int selectedItem() const
+ {
+ return m_selectedItem;
+ }
+
+ int startDragArea() const
+ {
+ return m_startDragArea;
+ }
+
+ int endDragArea() const
+ {
+ return m_endDragArea;
+ }
+
+ QmlProfilerTimelineModelProxy *profilerModelProxy() const { return m_profilerModelProxy; }
+ void setProfilerModelProxy(QObject *profilerModelProxy)
+ {
+ if (m_profilerModelProxy) {
+ disconnect(m_profilerModelProxy, SIGNAL(expandedChanged()), this, SLOT(requestPaint()));
+ }
+ m_profilerModelProxy = qobject_cast<QmlProfilerTimelineModelProxy *>(profilerModelProxy);
+
+ if (m_profilerModelProxy) {
+ connect(m_profilerModelProxy, SIGNAL(expandedChanged()), this, SLOT(requestPaint()));
+ }
+ emit profilerModelProxyChanged(m_profilerModelProxy);
+ }
+
+ Q_INVOKABLE qint64 getDuration(int index) const;
+ Q_INVOKABLE QString getFilename(int index) const;
+ Q_INVOKABLE int getLine(int index) const;
+ Q_INVOKABLE QString getDetails(int index) const;
+ Q_INVOKABLE int getYPosition(int index) const;
+
+ Q_INVOKABLE void setRowExpanded(int rowIndex, bool expanded);
+
+ Q_INVOKABLE void selectNext();
+ Q_INVOKABLE void selectPrev();
+ Q_INVOKABLE int nextItemFromId(int eventId) const;
+ Q_INVOKABLE int prevItemFromId(int eventId) const;
+ Q_INVOKABLE void selectNextFromId(int eventId);
+ Q_INVOKABLE void selectPrevFromId(int eventId);
+
+signals:
+ void startTimeChanged(qint64 arg);
+ void endTimeChanged(qint64 arg);
+ void profilerModelProxyChanged(QmlProfilerTimelineModelProxy *list);
+ void selectionLockedChanged(bool locked);
+ void selectedItemChanged(int itemIndex);
+ void startDragAreaChanged(int startDragArea);
+ void endDragAreaChanged(int endDragArea);
+ void itemPressed(int pressedItem);
+
+public slots:
+ void clearData();
+ void requestPaint();
+
+
+ void setStartTime(qint64 arg)
+ {
+ if (m_startTime != arg) {
+ m_startTime = arg;
+ emit startTimeChanged(arg);
+ }
+ }
+
+ void setEndTime(qint64 arg)
+ {
+ if (m_endTime != arg) {
+ m_endTime = arg;
+ emit endTimeChanged(arg);
+ }
+ }
+
+ void setSelectionLocked(bool locked)
+ {
+ if (m_selectionLocked != locked) {
+ m_selectionLocked = locked;
+ update();
+ emit selectionLockedChanged(locked);
+ }
+ }
+
+ void setSelectedItem(int itemIndex)
+ {
+ if (m_selectedItem != itemIndex) {
+ m_selectedItem = itemIndex;
+ update();
+ emit selectedItemChanged(itemIndex);
+ }
+ }
+
+ void setStartDragArea(int startDragArea)
+ {
+ if (m_startDragArea != startDragArea) {
+ m_startDragArea = startDragArea;
+ emit startDragAreaChanged(startDragArea);
+ }
+ }
+
+ void setEndDragArea(int endDragArea)
+ {
+ if (m_endDragArea != endDragArea) {
+ m_endDragArea = endDragArea;
+ emit endDragAreaChanged(endDragArea);
+ }
+ }
+
+protected:
+ virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *);
+ virtual void componentComplete();
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
+
+private:
+ QColor colorForItem(int itemIndex);
+ void drawItemsToPainter(QPainter *p, int fromIndex, int toIndex);
+ void drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex);
+ void drawBindingLoopMarkers(QPainter *p, int fromIndex, int toIndex);
+
+ void manageClicked();
+ void manageHovered(int x, int y);
+
+private:
+ qint64 m_startTime;
+ qint64 m_endTime;
+ qreal m_spacing;
+ qint64 m_lastStartTime;
+ qint64 m_lastEndTime;
+
+ QmlProfilerTimelineModelProxy *m_profilerModelProxy;
+
+ QList<int> m_rowLastX;
+ QList<int> m_rowStarts;
+ QList<int> m_rowWidths;
+ QList<bool> m_rowsExpanded;
+
+ struct {
+ qint64 startTime;
+ qint64 endTime;
+ int row;
+ int eventIndex;
+ } m_currentSelection;
+
+ int m_selectedItem;
+ bool m_selectionLocked;
+ int m_startDragArea;
+ int m_endDragArea;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+QML_DECLARE_TYPE(QmlProfiler::Internal::TimelineRenderer)
+
+#endif // TIMELINERENDERER_H
diff --git a/qmlprofiler.pro b/qmlprofiler.pro
new file mode 100644
index 0000000000..69b40ff79a
--- /dev/null
+++ b/qmlprofiler.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+
+SUBDIRS += plugins/qmlprofiler
diff --git a/qtcreatorplugin.pri b/qtcreatorplugin.pri
new file mode 100644
index 0000000000..adf4faec63
--- /dev/null
+++ b/qtcreatorplugin.pri
@@ -0,0 +1,7 @@
+IDE_SOURCE_TREE=$$(IDE_SOURCE_TREE)
+IDE_BUILD_TREE=$$(IDE_BUILD_TREE)
+
+isEmpty(IDE_SOURCE_TREE):error(Set IDE_SOURCE_TREE environment variable)
+isEmpty(IDE_BUILD_TREE):error(Set IDE_BUILD_TREE environment variable)
+
+include($$IDE_SOURCE_TREE/src/qtcreatorplugin.pri)