From d2911d70f36b7fd1de4ebbd41915782669f8d721 Mon Sep 17 00:00:00 2001
From: Christiaan Janssen <christiaan.janssen@nokia.com>
Date: Tue, 26 Jul 2011 13:56:14 +0200
Subject: [PATCH] QmlProfiler: New event list with caching, load, save

Change-Id: I640a16649156a49f2d7e7006d6b2ea38fe218620
Reviewed-on: http://codereview.qt.nokia.com/3043
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Kai Koehne <kai.koehne@nokia.com>
---
 .../qmlprofilertraceclient.cpp                |  13 +-
 .../qmljsdebugclient/qmlprofilertraceclient.h |   2 +-
 src/plugins/qmlprofiler/qml/MainView.js       |  26 +-
 src/plugins/qmlprofiler/qml/MainView.qml      | 108 +--
 src/plugins/qmlprofiler/qml/StatusDisplay.qml |   9 +-
 src/plugins/qmlprofiler/qml/TimeDisplay.qml   |   2 +-
 src/plugins/qmlprofiler/qmlprofiler.pro       |   6 +-
 .../qmlprofiler/qmlprofilereventlist.cpp      | 849 ++++++++++++++++++
 .../qmlprofiler/qmlprofilereventlist.h        | 138 +++
 .../qmlprofiler/qmlprofilereventview.cpp      | 213 +----
 .../qmlprofiler/qmlprofilereventview.h        |  61 +-
 src/plugins/qmlprofiler/qmlprofilertool.cpp   |  58 +-
 src/plugins/qmlprofiler/qmlprofilertool.h     |   6 +
 src/plugins/qmlprofiler/timelineview.cpp      | 320 ++++---
 src/plugins/qmlprofiler/timelineview.h        |  49 +-
 src/plugins/qmlprofiler/tracewindow.cpp       |  35 +-
 src/plugins/qmlprofiler/tracewindow.h         |  11 +-
 17 files changed, 1417 insertions(+), 489 deletions(-)
 create mode 100644 src/plugins/qmlprofiler/qmlprofilereventlist.cpp
 create mode 100644 src/plugins/qmlprofiler/qmlprofilereventlist.h

diff --git a/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp b/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp
index 6042f38a566..62e8bd58782 100644
--- a/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp
+++ b/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp
@@ -40,10 +40,8 @@ public:
         : inProgressRanges(0)
         , maximumTime(0)
         , recording(false)
-        , nestingLevel(0)
     {
         ::memset(rangeCount, 0, MaximumQmlEventType * sizeof(int));
-        ::memset(nestingInType, 0, MaximumQmlEventType * sizeof(int));
     }
 
     qint64 inProgressRanges;
@@ -53,8 +51,6 @@ public:
     int rangeCount[MaximumQmlEventType];
     qint64 maximumTime;
     bool recording;
-    int nestingLevel;
-    int nestingInType[MaximumQmlEventType];
 };
 
 } // namespace QmlJsDebugClient
@@ -77,8 +73,6 @@ QmlProfilerTraceClient::~QmlProfilerTraceClient()
 void QmlProfilerTraceClient::clearData()
 {
     ::memset(d->rangeCount, 0, MaximumQmlEventType * sizeof(int));
-    ::memset(d->nestingInType, 0, MaximumQmlEventType * sizeof(int));
-    d->nestingLevel = 0;
     emit cleared();
 }
 
@@ -151,8 +145,6 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
             d->rangeStartTimes[range].push(time);
             d->inProgressRanges |= (static_cast<qint64>(1) << range);
             ++d->rangeCount[range];
-            ++d->nestingLevel;
-            ++d->nestingInType[range];
         } else if (messageType == RangeData) {
             QString data;
             stream >> data;
@@ -183,10 +175,7 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
                 Location location = d->rangeLocations[range].count() ? d->rangeLocations[range].pop() : Location();
 
                 qint64 startTime = d->rangeStartTimes[range].pop();
-                emit this->range((QmlEventType)range, d->nestingLevel, d->nestingInType[range], startTime,
-                                 time - startTime, data, location.fileName, location.line);
-                --d->nestingLevel;
-                --d->nestingInType[range];
+                emit this->range((QmlEventType)range, startTime, time - startTime, data, location.fileName, location.line);
                 if (d->rangeCount[range] == 0) {
                     int count = d->rangeDatas[range].count() +
                                 d->rangeStartTimes[range].count() +
diff --git a/src/libs/qmljsdebugclient/qmlprofilertraceclient.h b/src/libs/qmljsdebugclient/qmlprofilertraceclient.h
index abb64afd037..ca596b706ad 100644
--- a/src/libs/qmljsdebugclient/qmlprofilertraceclient.h
+++ b/src/libs/qmljsdebugclient/qmlprofilertraceclient.h
@@ -91,7 +91,7 @@ signals:
     void complete();
     void gap(qint64 time);
     void event(int event, qint64 time);
-    void range(int type, int nestingLevel, int nestingInType, qint64 startTime, qint64 length,
+    void range(int type, qint64 startTime, qint64 length,
                const QStringList &data, const QString &fileName, int line);
 
     void recordingChanged(bool arg);
diff --git a/src/plugins/qmlprofiler/qml/MainView.js b/src/plugins/qmlprofiler/qml/MainView.js
index 128ea396a33..a95df7daeef 100644
--- a/src/plugins/qmlprofiler/qml/MainView.js
+++ b/src/plugins/qmlprofiler/qml/MainView.js
@@ -32,10 +32,9 @@
 
 .pragma library
 
-var ranges = [ ];
 var xmargin = 0;
 var ymargin = 0;
-var nestingDepth = [];
+var qmlEventList = 0;
 
 var names = [ "Painting", "Compiling", "Creating", "Binding", "Handling Signal"]
 //### need better way to manipulate color from QML. In the meantime, these need to be kept in sync.
@@ -45,10 +44,8 @@ var xRayColors = [ "#6699CCB3", "#6699CCCC", "#6699B3CC", "#669999CC", "#66CC99B
 
 function reset()
 {
-    ranges = [];
     xmargin = 0;
     ymargin = 0;
-    nestingDepth = [];
 }
 
 //draw background of the graph
@@ -65,26 +62,26 @@ function drawGraph(canvas, ctxt, region)
 //draw the actual data to be graphed
 function drawData(canvas, ctxt, region)
 {
-    if (ranges.length == 0)
+    if ((!qmlEventList) || qmlEventList.count() == 0)
         return;
 
     var width = canvas.canvasSize.width - xmargin;
     var height = canvas.height - ymargin;
 
-    var sumValue = ranges[ranges.length - 1].start + ranges[ranges.length - 1].duration - ranges[0].start;
+    var sumValue = qmlEventList.lastTimeMark() - qmlEventList.firstTimeMark();
     var spacing = width / sumValue;
 
     ctxt.fillStyle = "rgba(0,0,0,1)";
     var highest = [0,0,0,0,0];
 
     //### only draw those in range
-    for (var ii = 0; ii < ranges.length; ++ii) {
+    for (var ii = 0; ii < qmlEventList.count(); ++ii) {
 
-        var xx = (ranges[ii].start - ranges[0].start) * spacing + xmargin;
+        var xx = (qmlEventList.getStartTime(ii) - qmlEventList.firstTimeMark()) * spacing + xmargin;
         if (xx > region.x + region.width)
             continue;
 
-        var size = ranges[ii].duration * spacing;
+        var size = qmlEventList.getDuration(ii) * spacing;
         if (xx + size < region.x)
             continue;
 
@@ -92,9 +89,10 @@ function drawData(canvas, ctxt, region)
             size = 0.5;
 
         xx = Math.round(xx);
-        if (xx + size > highest[ranges[ii].type]) {
-            ctxt.fillRect(xx, ranges[ii].type*10, size, 10);
-            highest[ranges[ii].type] = xx+size;
+        var ty = qmlEventList.getType(ii);
+        if (xx + size > highest[ty]) {
+            ctxt.fillRect(xx, ty*10, size, 10);
+            highest[ty] = xx+size;
         }
     }
 }
@@ -107,12 +105,12 @@ function plot(canvas, ctxt, region)
 
 function xScale(canvas)
 {
-    if (ranges.length === 0)
+    if ((!qmlEventList) || qmlEventList.count() == 0)
         return;
 
     var width = canvas.canvasSize.width - xmargin;
 
-    var sumValue = ranges[ranges.length - 1].start + ranges[ranges.length - 1].duration - ranges[0].start;
+    var sumValue = qmlEventList.lastTimeMark() - qmlEventList.firstTimeMark();
     var spacing = sumValue / width;
     return spacing;
 }
diff --git a/src/plugins/qmlprofiler/qml/MainView.qml b/src/plugins/qmlprofiler/qml/MainView.qml
index 72e026b5d77..025eac39b57 100644
--- a/src/plugins/qmlprofiler/qml/MainView.qml
+++ b/src/plugins/qmlprofiler/qml/MainView.qml
@@ -38,7 +38,8 @@ Rectangle {
     id: root
 
     property bool dataAvailable: true;
-    property int eventCount: Plotter.ranges.length;
+    property int eventCount: 0;
+    property real progress: 0;
 
     // move the cursor in the editor
     signal updateCursorPosition
@@ -72,34 +73,35 @@ Rectangle {
     property bool mouseTracking: false;
 
     onSelectedEventIndexChanged: {
-        if ((!mouseTracking) && Plotter.ranges.length > 0
-                && selectedEventIndex > -1 && selectedEventIndex < Plotter.ranges.length) {
+        if ((!mouseTracking) && eventCount > 0
+                && selectedEventIndex > -1 && selectedEventIndex < eventCount) {
             // re-center flickable if necessary
-            var event = Plotter.ranges[selectedEventIndex];
             var xs = Plotter.xScale(canvas);
-            var startTime = Plotter.ranges[0].start;
-            if (rangeMover.value + startTime> event.start) {
+            var startTime = qmlEventList.firstTimeMark();
+            var eventStartTime = qmlEventList.getStartTime(selectedEventIndex);
+            var eventDuration = qmlEventList.getDuration(selectedEventIndex);
+            if (rangeMover.value + startTime > eventStartTime) {
                 rangeMover.x = Math.max(0,
-                    Math.floor((event.start - startTime) / xs - canvas.canvasWindow.x - rangeMover.zoomWidth/2) );
-            } else if (rangeMover.value + startTime + rangeMover.zoomWidth * xs < event.start + event.duration) {
-                rangeMover.x = Math.floor((event.start + event.duration - startTime) / xs - canvas.canvasWindow.x - rangeMover.zoomWidth/2);
+                    Math.floor((eventStartTime - startTime) / xs - canvas.canvasWindow.x - rangeMover.zoomWidth/2) );
+            } else if (rangeMover.value + startTime + rangeMover.zoomWidth * xs < eventStartTime + eventDuration) {
+                rangeMover.x = Math.floor((eventStartTime + eventDuration - startTime) / xs - canvas.canvasWindow.x - rangeMover.zoomWidth/2);
             }
         }
     }
 
     function nextEvent() {
-        if (Plotter.ranges.length > 0) {
+        if (eventCount > 0) {
             ++selectedEventIndex;
-            if (selectedEventIndex >= Plotter.ranges.length)
+            if (selectedEventIndex >= eventCount)
                 selectedEventIndex = 0;
         }
     }
 
     function prevEvent() {
-        if (Plotter.ranges.length > 0) {
+        if (eventCount > 0) {
             --selectedEventIndex;
             if (selectedEventIndex < 0)
-                selectedEventIndex = Plotter.ranges.length - 1;
+                selectedEventIndex = eventCount - 1;
         }
     }
 
@@ -127,46 +129,29 @@ Rectangle {
         rangeMover.updateZoomControls();
     }
 
-    //handle debug data coming from C++
     Connections {
-        target: connection
-
-        onRange: {
-            if (root.dataAvailable) {
-                root.clearData();
-            }
-
-            // todo: consider nestingLevel
-            if (!root.dataAvailable) {
-                if (!Plotter.nestingDepth[type])
-                    Plotter.nestingDepth[type] = nestingInType;
-                else
-                    Plotter.nestingDepth[type] = Math.max(Plotter.nestingDepth[type], nestingInType);
-                Plotter.ranges.push( { type: type, start: startTime, duration: length, label: data, fileName: fileName, line: line, nestingLevel: nestingInType, nestingDepth: Plotter.nestingDepth[type] } );
-                if (nestingInType == 1)
-                    Plotter.nestingDepth[type] = 1;
-                root.eventCount = Plotter.ranges.length;
-
-            }
+        target: qmlEventList
+        onCountChanged: {
+            eventCount = qmlEventList.count();
+            if (eventCount == 0)
+                root.clearAll();
+            if (eventCount > 1) {
+                root.progress = Math.min(1.0,
+                    (qmlEventList.lastTimeMark() - qmlEventList.firstTimeMark()) / root.elapsedTime * 1e-9 ) * 0.5;
+            } else
+            root.progress = 0;
         }
 
-        onComplete: {
-            root.dataAvailable = true;
-            if (Plotter.ranges.length > 0) {
-                view.visible = true;
-                view.setRanges(Plotter.ranges);
-                view.updateTimeline();
-                canvas.requestPaint();
-                rangeMover.x = 1    //### hack to get view to display things immediately
-                rangeMover.x = 0
-                rangeMover.opacity = 1
-            }
+        onParsingStatusChanged: {
+            root.dataAvailable = false;
         }
 
-        onDataCleared: {
-            root.clearAll();
+        onDataReady: {
+            if (eventCount > 0) {
+                view.clearData();
+                view.rebuildCache();
+            }
         }
-
     }
 
     // Elapsed
@@ -198,8 +183,8 @@ Rectangle {
         height: flick.height + labels.y
         anchors.left: flick.left
         anchors.right: flick.right
-        startTime: rangeMover.x * Plotter.xScale(canvas);
-        endTime: (rangeMover.x + rangeMover.zoomWidth) * Plotter.xScale(canvas);
+        startTime: rangeMover.x * Plotter.xScale(canvas) + qmlEventList.firstTimeMark();
+        endTime: (rangeMover.x + rangeMover.zoomWidth) * Plotter.xScale(canvas) + qmlEventList.firstTimeMark();
     }
 
     function hideRangeDetails() {
@@ -241,6 +226,9 @@ Rectangle {
         TimelineView {
             id: view
 
+            eventList: qmlEventList;
+            onEventListChanged: Plotter.qmlEventList = qmlEventList;
+
             width: flick.width;
             height: flick.contentHeight;
 
@@ -256,6 +244,20 @@ Rectangle {
             endTime: startTime + (rangeMover.zoomWidth*Plotter.xScale(canvas))
             onEndTimeChanged: updateTimeline()
 
+            onCachedProgressChanged: root.progress = 0.5 + cachedProgress * 0.5;
+            onCacheReady: {
+                root.progress = 1.0;
+                root.dataAvailable = true;
+                if (root.eventCount > 0) {
+                    view.visible = true;
+                    view.updateTimeline();
+                    canvas.requestPaint();
+                    rangeMover.x = 1    //### hack to get view to display things immediately
+                    rangeMover.x = 0
+                    rangeMover.opacity = 1
+                }
+            }
+
             delegate: Rectangle {
                 id: obj
 
@@ -298,10 +300,10 @@ Rectangle {
 
                 function enableSelected(x,y) {
                     myColor = Qt.darker(baseColor, 1.2)
-                    rangeDetails.duration = duration
-                    rangeDetails.label = label
-                    rangeDetails.file = fileName
-                    rangeDetails.line = line
+                    rangeDetails.duration = qmlEventList.getDuration(index)/1000.0;
+                    rangeDetails.label = qmlEventList.getDetails(index);
+                    rangeDetails.file = qmlEventList.getFilename(index);
+                    rangeDetails.line = qmlEventList.getLine(index);
                     rangeDetails.type = Plotter.names[type]
 
                     var margin = 10;
diff --git a/src/plugins/qmlprofiler/qml/StatusDisplay.qml b/src/plugins/qmlprofiler/qml/StatusDisplay.qml
index f09e886fa51..9089e42134a 100644
--- a/src/plugins/qmlprofiler/qml/StatusDisplay.qml
+++ b/src/plugins/qmlprofiler/qml/StatusDisplay.qml
@@ -4,14 +4,7 @@ import "MainView.js" as Plotter
 Rectangle {
     id: statusDisplay
 
-    property real percentage : 0
-    property int eventCount:  root.eventCount
-    onEventCountChanged:  {
-        if (state=="loading" && eventCount > 0 && root.elapsedTime > 0) {
-            percentage = Math.min(1.0,
-                (Plotter.ranges[Plotter.ranges.length-1].start - Plotter.ranges[0].start) / root.elapsedTime * 1e-9 );
-        }
-    }
+    property real percentage : root.progress
 
     width: Math.max(200, statusText.width+20);
     height: displayColumn.height + 20
diff --git a/src/plugins/qmlprofiler/qml/TimeDisplay.qml b/src/plugins/qmlprofiler/qml/TimeDisplay.qml
index 3b5c67852c7..56c816245a5 100644
--- a/src/plugins/qmlprofiler/qml/TimeDisplay.qml
+++ b/src/plugins/qmlprofiler/qml/TimeDisplay.qml
@@ -164,7 +164,7 @@ TiledCanvas {
         }
 
         onMousePositionChanged: {
-            if (!Plotter.ranges.length)
+            if (!root.eventCount)
                 return;
 
             if (!pressed && timeDisplayEnd.visible)
diff --git a/src/plugins/qmlprofiler/qmlprofiler.pro b/src/plugins/qmlprofiler/qmlprofiler.pro
index 107acdcfc5a..231d4bf36cb 100644
--- a/src/plugins/qmlprofiler/qmlprofiler.pro
+++ b/src/plugins/qmlprofiler/qmlprofiler.pro
@@ -27,7 +27,8 @@ SOURCES += \
     codaqmlprofilerrunner.cpp \
     remotelinuxqmlprofilerrunner.cpp \
     qmlprofilereventview.cpp \
-    qmlprofilerruncontrolfactory.cpp
+    qmlprofilerruncontrolfactory.cpp \
+    qmlprofilereventlist.cpp
 
 HEADERS += \
     qmlprofilerconstants.h \
@@ -43,7 +44,8 @@ HEADERS += \
     codaqmlprofilerrunner.h \
     remotelinuxqmlprofilerrunner.h \
     qmlprofilereventview.h \
-    qmlprofilerruncontrolfactory.h
+    qmlprofilerruncontrolfactory.h\
+    qmlprofilereventlist.h
 
 RESOURCES += \
     qml/qml.qrc
diff --git a/src/plugins/qmlprofiler/qmlprofilereventlist.cpp b/src/plugins/qmlprofiler/qmlprofilereventlist.cpp
new file mode 100644
index 00000000000..dd05407dbc1
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilereventlist.cpp
@@ -0,0 +1,849 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#include "qmlprofilereventlist.h"
+
+#include <coreplugin/icore.h>
+#include <QtCore/QUrl>
+#include <QtCore/QHash>
+#include <QtCore/QtAlgorithms>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+
+#include <QtCore/QFile>
+#include <QtCore/QXmlStreamReader>
+#include <QtCore/QXmlStreamWriter>
+
+#include <QtCore/QTimer>
+#include <QtGui/QMainWindow>
+#include <QtGui/QMessageBox>
+
+#include <QDebug>
+
+namespace QmlProfiler {
+namespace Internal {
+
+#define MIN_LEVEL 1
+
+// description
+struct QmlEventDescription {
+    QmlEventDescription() : displayname(0), location(0), filename(0), details(0) {}
+    ~QmlEventDescription() {
+        delete displayname;
+        delete location;
+        delete filename;
+        delete details;
+    }
+
+    QString *displayname;
+    QString *location;
+    QString *filename;
+    QString *details;
+    int eventType;
+    int line;
+};
+
+// endtimedata
+struct QmlEventEndTimeData {
+    qint64 endTime;
+    int startTimeIndex;
+    QmlEventData *description;
+};
+
+// starttimedata
+struct QmlEventStartTimeData {
+    qint64 startTime;
+    qint64 length;
+    qint64 level;
+    int endTimeIndex;
+    qint64 nestingLevel;
+    qint64 nestingDepth;
+    QmlEventData *description;
+
+};
+
+// used by quicksort
+bool compareEndTimes(const QmlEventEndTimeData &t1, const QmlEventEndTimeData &t2)
+{
+    return t1.endTime < t2.endTime;
+}
+
+bool compareStartTimes(const QmlEventStartTimeData &t1, const QmlEventStartTimeData &t2)
+{
+    return t1.startTime < t2.startTime;
+}
+
+bool compareStartIndexes(const QmlEventEndTimeData &t1, const QmlEventEndTimeData &t2)
+{
+    return t1.startTimeIndex < t2.startTimeIndex;
+}
+
+class QmlProfilerEventList::QmlProfilerEventListPrivate
+{
+public:
+    QmlProfilerEventListPrivate(QmlProfilerEventList *qq) : q(qq) {}
+
+    QmlProfilerEventList *q;
+
+    // Stored data
+    QmlEventHash m_eventDescriptions;
+    QList<QmlEventEndTimeData> m_endTimeSortedList;
+    QList<QmlEventStartTimeData> m_startTimeSortedList;
+
+    // file to load
+    QString m_filename;
+    ParsingStatus m_parsingStatus;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+
+QmlProfilerEventList::QmlProfilerEventList(QObject *parent) :
+    QObject(parent), d(new QmlProfilerEventListPrivate(this))
+{
+    d->m_parsingStatus = DoneStatus;
+    setObjectName("QmlProfilerEventStatistics");
+}
+
+QmlProfilerEventList::~QmlProfilerEventList()
+{
+    clear();
+}
+
+void QmlProfilerEventList::clear()
+{
+    foreach (QmlEventData *binding, d->m_eventDescriptions.values())
+        delete binding;
+    d->m_eventDescriptions.clear();
+
+    d->m_endTimeSortedList.clear();
+    d->m_startTimeSortedList.clear();
+    emit countChanged();
+}
+
+QList <QmlEventData *> QmlProfilerEventList::getEventDescriptions() const
+{
+    return d->m_eventDescriptions.values();
+}
+
+void QmlProfilerEventList::addRangedEvent(int type, qint64 startTime, qint64 length,
+                                                const QStringList &data, const QString &fileName, int line)
+{
+    setParsingStatus(GettingDataStatus);
+
+    const QChar colon = QLatin1Char(':');
+    QString displayName, location, details;
+
+    if (fileName.isEmpty()) {
+        displayName = tr("<bytecode>");
+        location = QString("--:%1:%2").arg(QString::number(type), data.join(" "));
+    } else {
+        const QString filePath = QUrl(fileName).path();
+        displayName = filePath.mid(filePath.lastIndexOf(QChar('/')) + 1) + colon + QString::number(line);
+        location = fileName+colon+QString::number(line);
+    }
+
+    QmlEventData *newEvent;
+    if (d->m_eventDescriptions.contains(location)) {
+        newEvent = d->m_eventDescriptions[location];
+    } else {
+
+        // generate details string
+        if (data.isEmpty())
+            details = tr("Source code not available");
+        else {
+            details = data.join(" ").replace('\n'," ").simplified();
+            QRegExp rewrite("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)");
+            bool match = rewrite.exactMatch(details);
+            if (match) {
+                details = rewrite.cap(1) + ": " + rewrite.cap(3);
+            }
+            if (details.startsWith(QString("file://")))
+                details = details.mid(details.lastIndexOf(QChar('/')) + 1);
+        }
+
+        newEvent = new QmlEventData;
+        newEvent->displayname = displayName;
+        newEvent->filename = fileName;
+        newEvent->location = location;
+        newEvent->line = line;
+        newEvent->eventType = (QmlJsDebugClient::QmlEventType)type;
+        newEvent->details = details;
+        d->m_eventDescriptions.insert(location, newEvent);
+    }
+
+    QmlEventEndTimeData endTimeData;
+    endTimeData.endTime = startTime + length;
+    endTimeData.description = newEvent;
+    endTimeData.startTimeIndex = d->m_startTimeSortedList.count();
+
+    QmlEventStartTimeData startTimeData;
+    startTimeData.startTime = startTime;
+    startTimeData.length = length;
+    startTimeData.description = newEvent;
+    startTimeData.endTimeIndex = d->m_endTimeSortedList.count();
+
+    d->m_endTimeSortedList << endTimeData;
+    d->m_startTimeSortedList << startTimeData;
+
+    emit countChanged();
+}
+
+void QmlProfilerEventList::complete()
+{
+    postProcess();
+}
+
+void QmlProfilerEventList::compileStatistics()
+{
+    // clear existing statistics
+    foreach (QmlEventData *eventDescription, d->m_eventDescriptions.values()) {
+        eventDescription->calls = 0;
+        // maximum possible value
+        eventDescription->minTime = d->m_endTimeSortedList.last().endTime;
+        eventDescription->maxTime = 0;
+        eventDescription->cumulatedDuration = 0;
+        eventDescription->parentList.clear();
+        eventDescription->childrenList.clear();
+    }
+
+    // compute parent-child relationship and call count
+    QHash<int, QmlEventData*> lastParent;
+    foreach (QmlEventStartTimeData eventStartData, d->m_startTimeSortedList) {
+        QmlEventData *eventDescription = eventStartData.description;
+        eventDescription->calls++;
+        eventDescription->cumulatedDuration += eventStartData.length;
+        if (eventDescription->maxTime < eventStartData.length)
+            eventDescription->maxTime = eventStartData.length;
+        if (eventDescription->minTime > eventStartData.length)
+            eventDescription->minTime = eventStartData.length;
+
+
+        if (eventStartData.level > MIN_LEVEL) {
+            if (lastParent.contains(eventStartData.level-1)) {
+                QmlEventData *parentEvent = lastParent[eventStartData.level-1];
+                if (!eventDescription->parentList.contains(parentEvent))
+                    eventDescription->parentList.append(parentEvent);
+                if (!parentEvent->childrenList.contains(eventDescription))
+                    parentEvent->childrenList.append(eventDescription);
+            }
+        }
+
+        lastParent[eventStartData.level] = eventDescription;
+    }
+
+    // compute percentages
+    double totalTime = 0;
+    foreach (QmlEventData *binding, d->m_eventDescriptions.values()) {
+        if (binding->filename.isEmpty())
+            continue;
+        totalTime += binding->cumulatedDuration;
+    }
+
+    foreach (QmlEventData *binding, d->m_eventDescriptions.values()) {
+        if (binding->filename.isEmpty())
+            continue;
+        binding->percentOfTime = binding->cumulatedDuration * 100.0 / totalTime;
+        binding->timePerCall = binding->calls > 0 ? double(binding->cumulatedDuration) / binding->calls : 0;
+    }
+
+    // continue postprocess
+    postProcess();
+}
+
+void QmlProfilerEventList::sortStartTimes()
+{
+    if (d->m_startTimeSortedList.count() < 2)
+        return;
+
+    // assuming startTimes is partially sorted
+    // identify blocks of events and sort them with quicksort
+    QList<QmlEventStartTimeData>::iterator itFrom = d->m_startTimeSortedList.end() - 2;
+    QList<QmlEventStartTimeData>::iterator itTo = d->m_startTimeSortedList.end() - 1;
+
+    while (itFrom != d->m_startTimeSortedList.begin() && itTo != d->m_startTimeSortedList.begin()) {
+        // find block to sort
+        while ( itFrom != d->m_startTimeSortedList.begin()
+                && itTo->startTime > itFrom->startTime ) {
+            itTo--;
+            itFrom = itTo - 1;
+        }
+
+        // if we're at the end of the list
+        if (itFrom == d->m_startTimeSortedList.begin())
+            break;
+
+        // find block length
+        while ( itFrom != d->m_startTimeSortedList.begin()
+                && itTo->startTime <= itFrom->startTime )
+            itFrom--;
+
+        if (itTo->startTime <= itFrom->startTime)
+            qSort(itFrom, itTo + 1, compareStartTimes);
+        else
+            qSort(itFrom + 1, itTo + 1, compareStartTimes);
+
+        // move to next block
+        itTo = itFrom;
+        itFrom = itTo - 1;
+    }
+
+    // link back the endTimes
+    for (int i = 0; i < d->m_startTimeSortedList.length(); i++)
+       d->m_endTimeSortedList[d->m_startTimeSortedList[i].endTimeIndex].startTimeIndex = i;
+
+    // continue postprocess
+    postProcess();
+}
+
+void QmlProfilerEventList::sortEndTimes()
+{
+    // assuming endTimes is partially sorted
+    // identify blocks of events and sort them with quicksort
+
+    if (d->m_endTimeSortedList.count() < 2)
+        return;
+
+    QList<QmlEventEndTimeData>::iterator itFrom = d->m_endTimeSortedList.begin();
+    QList<QmlEventEndTimeData>::iterator itTo = d->m_endTimeSortedList.begin() + 1;
+
+    while (itTo != d->m_endTimeSortedList.end() && itFrom != d->m_endTimeSortedList.end()) {
+        // find block to sort
+        while ( itTo != d->m_endTimeSortedList.end()
+                && d->m_startTimeSortedList[itTo->startTimeIndex].startTime >
+                d->m_startTimeSortedList[itFrom->startTimeIndex].startTime +
+                d->m_startTimeSortedList[itFrom->startTimeIndex].length ) {
+            itFrom++;
+            itTo = itFrom+1;
+        }
+
+        // if we're at the end of the list
+        if (itTo == d->m_endTimeSortedList.end())
+            break;
+
+        // find block length
+        while ( itTo != d->m_endTimeSortedList.end()
+                && d->m_startTimeSortedList[itTo->startTimeIndex].startTime <=
+                d->m_startTimeSortedList[itFrom->startTimeIndex].startTime +
+                d->m_startTimeSortedList[itFrom->startTimeIndex].length )
+            itTo++;
+
+        // sort block
+        qSort(itFrom, itTo, compareEndTimes);
+
+        // move to next block
+        itFrom = itTo;
+        itTo = itFrom+1;
+
+    }
+
+    // link back the startTimes
+    for (int i = 0; i < d->m_endTimeSortedList.length(); i++)
+        d->m_startTimeSortedList[d->m_endTimeSortedList[i].startTimeIndex].endTimeIndex = i;
+
+    // continue postprocess
+    postProcess();
+}
+
+void QmlProfilerEventList::computeNestingLevels()
+{
+    // compute levels
+    QHash <int, qint64> endtimesPerLevel;
+    QList <int> nestingLevels;
+    QList < QHash <int, qint64> > endtimesPerNestingLevel;
+    int level = MIN_LEVEL;
+    endtimesPerLevel[MIN_LEVEL] = 0;
+
+    for (int i = 0; i < QmlJsDebugClient::MaximumQmlEventType; i++) {
+        nestingLevels << MIN_LEVEL;
+        QHash <int, qint64> dummyHash;
+        dummyHash[MIN_LEVEL] = 0;
+        endtimesPerNestingLevel << dummyHash;
+    }
+
+    for (int i=0; i<d->m_startTimeSortedList.count(); i++) {
+        qint64 st = d->m_startTimeSortedList[i].startTime;
+        int type = d->m_startTimeSortedList[i].description->eventType;
+
+        // general level
+        if (endtimesPerLevel[level] > st) {
+            level++;
+        } else {
+            while (level > MIN_LEVEL && endtimesPerLevel[level-1] <= st)
+                level--;
+        }
+        endtimesPerLevel[level] = st + d->m_startTimeSortedList[i].length;
+
+        // per type
+        if (endtimesPerNestingLevel[type][nestingLevels[type]] > st) {
+            nestingLevels[type]++;
+        } else {
+            while (nestingLevels[type] > MIN_LEVEL &&
+                   endtimesPerNestingLevel[type][nestingLevels[type]-1] <= st)
+                nestingLevels[type]--;
+        }
+        endtimesPerNestingLevel[type][nestingLevels[type]] = st + d->m_startTimeSortedList[i].length;
+
+        d->m_startTimeSortedList[i].level = level;
+        d->m_startTimeSortedList[i].nestingLevel = nestingLevels[type];
+    }
+}
+
+void QmlProfilerEventList::computeNestingDepth()
+{
+    QHash <int, int> nestingDepth;
+    for (int i = 0; i < d->m_endTimeSortedList.count(); i++) {
+        int type = d->m_endTimeSortedList[i].description->eventType;
+        int nestingInType = d->m_startTimeSortedList[ d->m_endTimeSortedList[i].startTimeIndex ].nestingLevel;
+        if (!nestingDepth.contains(type))
+            nestingDepth[type] = nestingInType;
+        else {
+            int nd = nestingDepth[type];
+            nestingDepth[type] = nd > nestingInType ? nd : nestingInType;
+        }
+
+        d->m_startTimeSortedList[ d->m_endTimeSortedList[i].startTimeIndex ].nestingDepth = nestingDepth[type];
+        if (nestingInType == MIN_LEVEL)
+            nestingDepth[type] = MIN_LEVEL;
+    }
+}
+
+void QmlProfilerEventList::postProcess()
+{
+    switch (d->m_parsingStatus) {
+    case GettingDataStatus: {
+        setParsingStatus(SortingListsStatus);
+        QTimer::singleShot(50, this, SLOT(sortStartTimes()));
+        break;
+    }
+    case SortingEndsStatus: {
+        setParsingStatus(SortingListsStatus);
+        QTimer::singleShot(50, this, SLOT(sortEndTimes()));
+        break;
+    }
+    case SortingListsStatus: {
+        setParsingStatus(ComputingLevelsStatus);
+        QTimer::singleShot(50, this, SLOT(computeLevels()));
+        break;
+    }
+    case ComputingLevelsStatus: {
+        setParsingStatus(CompilingStatisticsStatus);
+        QTimer::singleShot(50, this, SLOT(compileStatistics()));
+        break;
+    }
+    case CompilingStatisticsStatus: {
+        linkEndsToStarts();
+        setParsingStatus(DoneStatus);
+        emit dataReady();
+        break;
+    }
+    default: break;
+    }
+
+}
+
+void QmlProfilerEventList::setParsingStatus(ParsingStatus ps)
+{
+    if (d->m_parsingStatus != ps) {
+        d->m_parsingStatus = ps;
+        emit parsingStatusChanged();
+    }
+}
+
+ParsingStatus QmlProfilerEventList::getParsingStatus() const
+{
+    return d->m_parsingStatus;
+}
+
+void QmlProfilerEventList::linkEndsToStarts()
+{
+    for (int i = 0; i < d->m_startTimeSortedList.count(); i++)
+        d->m_endTimeSortedList[d->m_startTimeSortedList[i].endTimeIndex].startTimeIndex = i;
+}
+
+void QmlProfilerEventList::computeLevels()
+{
+    computeNestingLevels();
+    computeNestingDepth();
+    // continue postprocess
+    postProcess();
+}
+
+// get list of events between A and B:
+// find fist event with endtime after A -> aa
+// find last event with starttime before B -> bb
+// list is from parent of aa with level=0 to bb, in the "sorted by starttime" list
+int QmlProfilerEventList::findFirstIndex(qint64 startTime) const
+{
+    int candidate = -1;
+    // in the "endtime" list, find the first event that ends after startTime
+    if (d->m_endTimeSortedList.isEmpty())
+        return 0; // -1
+    if (d->m_endTimeSortedList.length() == 1 || d->m_endTimeSortedList.first().endTime >= startTime)
+        candidate = 0;
+    else
+        if (d->m_endTimeSortedList.last().endTime <= startTime)
+            return 0; // -1
+
+    if (candidate == -1)
+    {
+        int fromIndex = 0;
+        int toIndex = d->m_endTimeSortedList.count()-1;
+        while (toIndex - fromIndex > 1) {
+            int midIndex = (fromIndex + toIndex)/2;
+            if (d->m_endTimeSortedList[midIndex].endTime < startTime)
+                fromIndex = midIndex;
+            else
+                toIndex = midIndex;
+        }
+
+        candidate = toIndex;
+    }
+
+    int ndx = d->m_endTimeSortedList[candidate].startTimeIndex;
+
+    // and then go to the parent
+    while (d->m_startTimeSortedList[ndx].level != MIN_LEVEL && ndx > 0)
+        ndx--;
+
+    return ndx;
+}
+
+int QmlProfilerEventList::findLastIndex(qint64 endTime) const
+{
+    // in the "starttime" list, find the last event that starts before endtime
+    if (d->m_startTimeSortedList.isEmpty())
+        return 0; // -1
+    if (d->m_startTimeSortedList.first().startTime >= endTime)
+        return 0; // -1
+    if (d->m_startTimeSortedList.length() == 1)
+        return 0;
+    if (d->m_startTimeSortedList.last().startTime <= endTime)
+        return d->m_startTimeSortedList.count()-1;
+
+    int fromIndex = 0;
+    int toIndex = d->m_startTimeSortedList.count()-1;
+    while (toIndex - fromIndex > 1) {
+        int midIndex = (fromIndex + toIndex)/2;
+        if (d->m_startTimeSortedList[midIndex].startTime < endTime)
+            fromIndex = midIndex;
+        else
+            toIndex = midIndex;
+    }
+
+    return fromIndex;
+}
+
+qint64 QmlProfilerEventList::firstTimeMark() const
+{
+    if (d->m_startTimeSortedList.isEmpty())
+        return 0;
+    else {
+        return d->m_startTimeSortedList[0].startTime;
+    }
+}
+
+qint64 QmlProfilerEventList::lastTimeMark() const
+{
+    if (d->m_endTimeSortedList.isEmpty())
+        return 0;
+    else {
+        return d->m_endTimeSortedList.last().endTime;
+    }
+}
+
+int QmlProfilerEventList::count() const
+{
+    return d->m_startTimeSortedList.count();
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+
+void QmlProfilerEventList::save(const QString &filename) const
+{
+    if (count() == 0) {
+        showErrorDialog(tr("No data to save"));
+        return;
+    }
+
+    QFile file(filename);
+    if (!file.open(QIODevice::WriteOnly)) {
+        showErrorDialog(tr("Could not open %1 for writing").arg(filename));
+        return;
+    }
+
+    QXmlStreamWriter stream(&file);
+    stream.setAutoFormatting(true);
+    stream.writeStartDocument();
+
+    stream.writeStartElement("trace");
+
+    stream.writeStartElement("eventData");
+    foreach (const QmlEventData *eventData, d->m_eventDescriptions.values()) {
+        stream.writeStartElement("event");
+        stream.writeAttribute("index", QString::number(d->m_eventDescriptions.keys().indexOf(eventData->location)));
+        stream.writeTextElement("displayname", eventData->displayname);
+        stream.writeTextElement("type", QString::number(eventData->eventType));
+        if (!eventData->filename.isEmpty()) {
+            stream.writeTextElement("filename", eventData->filename);
+            stream.writeTextElement("line", QString::number(eventData->line));
+        }
+        stream.writeTextElement("details", eventData->details);
+        stream.writeEndElement();
+    }
+    stream.writeEndElement(); // eventData
+
+    stream.writeStartElement("eventList");
+    foreach (const QmlEventStartTimeData &rangedEvent, d->m_startTimeSortedList) {
+        stream.writeStartElement("range");
+        stream.writeAttribute("startTime", QString::number(rangedEvent.startTime));
+        stream.writeAttribute("duration", QString::number(rangedEvent.length));
+        stream.writeAttribute("eventIndex", QString::number(d->m_eventDescriptions.keys().indexOf(rangedEvent.description->location)));
+        stream.writeEndElement();
+    }
+    stream.writeEndElement(); // eventList
+
+    stream.writeEndElement(); // trace
+    stream.writeEndDocument();
+
+    file.close();
+}
+
+void QmlProfilerEventList::setFilename(const QString &filename)
+{
+    d->m_filename = filename;
+}
+
+void QmlProfilerEventList::load(const QString &filename)
+{
+    setFilename(filename);
+    load();
+}
+
+// "be strict in your output but tolerant in your inputs"
+void QmlProfilerEventList::load()
+{
+    QString filename = d->m_filename;
+
+    QFile file(filename);
+
+    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+        showErrorDialog(tr("Could not open %1 for reading").arg(filename));
+        return;
+    }
+
+    setParsingStatus(GettingDataStatus);
+
+    // erase current
+    clear();
+
+    QHash <int, QmlEventData *> descriptionBuffer;
+    QmlEventData *currentEvent = 0;
+    bool startTimesAreSorted = true;
+
+    QXmlStreamReader stream(&file);
+
+    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 == "event") {
+                    QXmlStreamAttributes attributes = stream.attributes();
+                    if (attributes.hasAttribute("index")) {
+                        int ndx = attributes.value("index").toString().toInt();
+                        if (!descriptionBuffer.value(ndx))
+                            descriptionBuffer[ndx] = new QmlEventData;
+                        currentEvent = descriptionBuffer[ndx];
+                    }
+                    break;
+                }
+                if (elementName == "range") {
+                    QmlEventStartTimeData rangedEvent;
+                    QXmlStreamAttributes attributes = stream.attributes();
+                    if (attributes.hasAttribute("startTime"))
+                        rangedEvent.startTime = attributes.value("startTime").toString().toLongLong();
+                    if (attributes.hasAttribute("duration"))
+                        rangedEvent.length = attributes.value("duration").toString().toLongLong();
+                    if (attributes.hasAttribute("eventIndex")) {
+                        int ndx = attributes.value("eventIndex").toString().toInt();
+                        if (!descriptionBuffer.value(ndx))
+                            descriptionBuffer[ndx] = new QmlEventData();
+                        rangedEvent.description = descriptionBuffer.value(ndx);
+                    }
+                    rangedEvent.endTimeIndex = d->m_endTimeSortedList.length();
+
+                    if (!d->m_startTimeSortedList.isEmpty()
+                            && rangedEvent.startTime < d->m_startTimeSortedList.last().startTime)
+                        startTimesAreSorted = false;
+                    d->m_startTimeSortedList << rangedEvent;
+
+                    QmlEventEndTimeData endTimeEvent;
+                    endTimeEvent.endTime = rangedEvent.startTime + rangedEvent.length;
+                    endTimeEvent.startTimeIndex = d->m_startTimeSortedList.length()-1;
+                    endTimeEvent.description = rangedEvent.description;
+                    d->m_endTimeSortedList << endTimeEvent;
+                    break;
+                }
+
+                // the remaining are eventdata elements
+                if (!currentEvent)
+                    break;
+                stream.readNext();
+                if (stream.tokenType() != QXmlStreamReader::Characters)
+                    break;
+
+                QString readData = stream.text().toString();
+
+                if (elementName == "displayname") {
+                    currentEvent->displayname = readData;
+                    break;
+                }
+                if (elementName == "type") {
+                    currentEvent->eventType = QmlJsDebugClient::QmlEventType(readData.toInt());
+                    break;
+                }
+                if (elementName == "filename") {
+                    currentEvent->filename = readData;
+                    break;
+                }
+                if (elementName == "line") {
+                    currentEvent->line = readData.toInt();
+                    break;
+                }
+                if (elementName == "details") {
+                    currentEvent->details = readData;
+                    break;
+                }
+                break;
+            }
+            case QXmlStreamReader::EndElement : {
+                if (elementName == "event")
+                    currentEvent = 0;
+                break;
+            }
+            default: break;
+        }
+    }
+
+    file.close();
+
+    if (stream.hasError()) {
+        showErrorDialog(tr("Error while parsing %1").arg(filename));
+        clear();
+        return;
+    }
+
+    stream.clear();
+
+    // move the buffered data to the details cache
+    foreach (QmlEventData *desc, descriptionBuffer.values()) {
+        QString location = QString("%1:%2:%3").arg(QString::number(desc->eventType), desc->displayname, desc->details);
+        desc->location = location;
+        d->m_eventDescriptions[location] = desc;
+    }
+
+    // sort startTimeSortedList
+    if (!startTimesAreSorted) {
+        qSort(d->m_startTimeSortedList.begin(), d->m_startTimeSortedList.end(), compareStartTimes);
+        for (int i = 0; i< d->m_startTimeSortedList.length(); i++) {
+            QmlEventStartTimeData startTimeData = d->m_startTimeSortedList[i];
+            d->m_endTimeSortedList[startTimeData.endTimeIndex].startTimeIndex = i;
+        }
+        qSort(d->m_endTimeSortedList.begin(), d->m_endTimeSortedList.end(), compareStartIndexes);
+    }
+
+    emit countChanged();
+
+    setParsingStatus(SortingEndsStatus);
+
+    descriptionBuffer.clear();
+
+    postProcess();
+}
+
+void QmlProfilerEventList::showErrorDialog(const QString &st ) const
+{
+    Core::ICore * const core = Core::ICore::instance();
+    QMessageBox *errorDialog = new QMessageBox(core->mainWindow());
+    errorDialog->setIcon(QMessageBox::Warning);
+    errorDialog->setWindowTitle(tr("QML Profiler"));
+    errorDialog->setText( st );
+    errorDialog->setStandardButtons(QMessageBox::Ok);
+    errorDialog->setDefaultButton(QMessageBox::Ok);
+    errorDialog->setModal(false);
+    errorDialog->show();
+}
+
+///////////////////////////////////////////////
+qint64 QmlProfilerEventList::getStartTime(int index) const {
+    return d->m_startTimeSortedList[index].startTime;
+}
+
+qint64 QmlProfilerEventList::getEndTime(int index) const {
+    return d->m_startTimeSortedList[index].startTime + d->m_startTimeSortedList[index].length;
+}
+
+qint64 QmlProfilerEventList::getDuration(int index) const {
+    return d->m_startTimeSortedList[index].length;
+}
+
+int QmlProfilerEventList::getType(int index) const {
+    return d->m_startTimeSortedList[index].description->eventType;
+}
+
+int QmlProfilerEventList::getNestingLevel(int index) const {
+    return d->m_startTimeSortedList[index].nestingLevel;
+}
+
+int QmlProfilerEventList::getNestingDepth(int index) const {
+    return d->m_startTimeSortedList[index].nestingDepth;
+}
+
+QString QmlProfilerEventList::getFilename(int index) const {
+    return d->m_startTimeSortedList[index].description->filename;
+}
+
+int QmlProfilerEventList::getLine(int index) const {
+    return d->m_startTimeSortedList[index].description->line;
+}
+
+QString QmlProfilerEventList::getDetails(int index) const {
+    return d->m_startTimeSortedList[index].description->details;
+}
+
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qmlprofilereventlist.h b/src/plugins/qmlprofiler/qmlprofilereventlist.h
new file mode 100644
index 00000000000..767da098d72
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilereventlist.h
@@ -0,0 +1,138 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#ifndef QMLPROFILEREVENTLIST_H
+#define QMLPROFILEREVENTLIST_H
+
+#include <QObject>
+#include <QtCore/QHash>
+#include "qmljsdebugclient/qmlprofilereventtypes.h"
+
+namespace QmlProfiler {
+namespace Internal {
+
+struct QmlEventData
+{
+    QString displayname;
+    QString filename;
+    QString location;
+    QString details;
+    int line;
+    QmlJsDebugClient::QmlEventType eventType;
+    QList< QmlEventData *> parentList;
+    QList< QmlEventData *> childrenList;
+    qint64 cumulatedDuration;
+    qint64 calls;
+    qint64 minTime;
+    qint64 maxTime;
+    double timePerCall;
+    double percentOfTime;
+};
+
+typedef QHash<QString, QmlEventData *> QmlEventHash;
+typedef QList<QmlEventData *> QmlEventDescriptions;
+
+enum ParsingStatus {
+    GettingDataStatus = 0,
+    SortingListsStatus = 1,
+    SortingEndsStatus = 2,
+    ComputingLevelsStatus = 3,
+    CompilingStatisticsStatus = 4,
+    DoneStatus = 5
+};
+
+class QmlProfilerEventList : public QObject
+{
+    Q_OBJECT
+public:
+
+    explicit QmlProfilerEventList(QObject *parent = 0);
+    ~QmlProfilerEventList();
+
+    QmlEventDescriptions getEventDescriptions() const;
+
+    int findFirstIndex(qint64 startTime) const;
+    int findLastIndex(qint64 endTime) const;
+    Q_INVOKABLE qint64 firstTimeMark() const;
+    Q_INVOKABLE qint64 lastTimeMark() const;
+
+    Q_INVOKABLE int count() const;
+    void setParsingStatus(ParsingStatus ps);
+    Q_INVOKABLE ParsingStatus getParsingStatus() const;
+
+    // data access
+    Q_INVOKABLE qint64 getStartTime(int index) const;
+    Q_INVOKABLE qint64 getEndTime(int index) const;
+    Q_INVOKABLE qint64 getDuration(int index) const;
+    Q_INVOKABLE int getType(int index) const;
+    Q_INVOKABLE int getNestingLevel(int index) const;
+    Q_INVOKABLE int getNestingDepth(int index) const;
+    Q_INVOKABLE QString getFilename(int index) const;
+    Q_INVOKABLE int getLine(int index) const;
+    Q_INVOKABLE QString getDetails(int index) const;
+
+    void showErrorDialog(const QString &st ) const;
+signals:
+    void dataReady();
+    void countChanged();
+    void parsingStatusChanged();
+
+public slots:
+    void clear();
+    void addRangedEvent(int type, qint64 startTime, qint64 length,
+                        const QStringList &data, const QString &fileName, int line);
+    void complete();
+    void save(const QString &filename) const;
+    void load(const QString &filename);
+    void setFilename(const QString &filename);
+    void load();
+
+private slots:
+    void postProcess();
+    void sortEndTimes();
+    void sortStartTimes();
+    void computeLevels();
+    void computeNestingLevels();
+    void computeNestingDepth();
+    void compileStatistics();
+    void linkEndsToStarts();
+
+private:
+    class QmlProfilerEventListPrivate;
+    QmlProfilerEventListPrivate *d;
+};
+
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILEREVENTLIST_H
diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.cpp b/src/plugins/qmlprofiler/qmlprofilereventview.cpp
index 28de7cb49e3..114c696e0d9 100644
--- a/src/plugins/qmlprofiler/qmlprofilereventview.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilereventview.cpp
@@ -35,14 +35,20 @@
 #include <QtCore/QUrl>
 #include <QtCore/QHash>
 
+#include <QtGui/QStandardItem>
 #include <QtGui/QHeaderView>
-#include <QtGui/QStandardItemModel>
+
+#include <QtGui/QContextMenuEvent>
+#include <QDebug>
+
 
 using namespace QmlJsDebugClient;
 
 namespace QmlProfiler {
 namespace Internal {
 
+////////////////////////////////////////////////////////////////////////////////////
+
 class EventsViewItem : public QStandardItem
 {
 public:
@@ -66,22 +72,6 @@ public:
 };
 
 
-////////////////////////////////////////////////////////////////////////////////////
-
-class QmlProfilerEventStatistics::QmlProfilerEventStatisticsPrivate
-{
-public:
-    QmlProfilerEventStatisticsPrivate(QmlProfilerEventStatistics *qq) : q(qq) {}
-
-    void postProcess();
-
-    QmlProfilerEventStatistics *q;
-    QmlEventHash m_rootHash;
-    QHash<int, QmlEventList> m_pendingEvents;
-    int m_lastLevel;
-};
-
-
 ////////////////////////////////////////////////////////////////////////////////////
 
 class QmlProfilerEventsView::QmlProfilerEventsViewPrivate
@@ -89,7 +79,7 @@ class QmlProfilerEventsView::QmlProfilerEventsViewPrivate
 public:
     QmlProfilerEventsViewPrivate(QmlProfilerEventsView *qq) : q(qq) {}
 
-    void buildModelFromList(const QmlEventList &list, QStandardItem *parentItem, const QmlEventList &visitedFunctionsList = QmlEventList() );
+    void buildModelFromList(const QmlEventDescriptions &list, QStandardItem *parentItem, const QmlEventDescriptions &visitedFunctionsList = QmlEventDescriptions() );
     int getFieldCount();
     QString displayTime(double time) const;
     QString nameForType(int typeNumber) const;
@@ -97,7 +87,7 @@ public:
 
     QmlProfilerEventsView *q;
 
-    QmlProfilerEventStatistics *m_eventStatistics;
+    QmlProfilerEventList *m_eventStatistics;
     QStandardItemModel *m_model;
     QList<bool> m_fieldShown;
     bool m_showAnonymous;
@@ -107,151 +97,7 @@ public:
 
 ////////////////////////////////////////////////////////////////////////////////////
 
-QmlProfilerEventStatistics::QmlProfilerEventStatistics(QObject *parent) :
-    QObject(parent), d(new QmlProfilerEventStatisticsPrivate(this))
-{
-    setObjectName("QmlProfilerEventStatistics");
-    d->m_lastLevel = -1;
-}
-
-QmlProfilerEventStatistics::~QmlProfilerEventStatistics()
-{
-    clear();
-}
-
-void QmlProfilerEventStatistics::clear()
-{
-    foreach (int levelNumber, d->m_pendingEvents.keys())
-        d->m_pendingEvents[levelNumber].clear();
-
-    d->m_lastLevel = -1;
-
-    foreach (QmlEventData *binding, d->m_rootHash.values())
-        delete binding;
-    d->m_rootHash.clear();
-}
-
-QList <QmlEventData *> QmlProfilerEventStatistics::getEventList() const
-{
-    return d->m_rootHash.values();
-}
-
-int QmlProfilerEventStatistics::eventCount() const
-{
-    return d->m_rootHash.size();
-}
-
-void QmlProfilerEventStatistics::addRangedEvent(int type, int nestingLevel, int nestingInType, qint64 startTime, qint64 length,
-                                                const QStringList &data, const QString &fileName, int line)
-{
-    Q_UNUSED(startTime);
-    Q_UNUSED(nestingInType);
-
-    const QChar colon = QLatin1Char(':');
-    QString displayName, location, details;
-
-    if (data.isEmpty())
-        details = tr("Source code not available");
-    else {
-        details = data.join(" ").replace('\n'," ").simplified();
-        QRegExp rewrite("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)");
-        bool match = rewrite.exactMatch(details);
-        if (match) {
-            details = rewrite.cap(1) + ": " + rewrite.cap(3);
-        }
-        if (details.startsWith(QString("file://")))
-            details = details.mid(details.lastIndexOf(QChar('/')) + 1);
-    }
-
-    if (fileName.isEmpty()) {
-        displayName = tr("<bytecode>");
-        location = QString("--:%1:%2").arg(QString::number(type), details);
-    } else {
-        const QString filePath = QUrl(fileName).path();
-        displayName = filePath.mid(filePath.lastIndexOf(QChar('/')) + 1) + colon + QString::number(line);
-        location = fileName+colon+QString::number(line);
-    }
-
-
-    // New Data:  if it's not in the hash, put it there
-    // if it's in the hash, get the reference from the hash
-    QmlEventData *newBinding;
-    QmlEventHash::iterator it = d->m_rootHash.find(location);
-    if (it != d->m_rootHash.end()) {
-        newBinding = it.value();
-        newBinding->duration += length;
-        newBinding->calls++;
-        if (newBinding->maxTime < length)
-            newBinding->maxTime = length;
-        if (newBinding->minTime > length)
-            newBinding->minTime = length;
-    } else {
-        newBinding = new QmlEventData;
-        newBinding->calls = 1;
-        newBinding->duration = length;
-        newBinding->displayname = new QString(displayName);
-        newBinding->filename = new QString(fileName);
-        newBinding->location = new QString(location);
-        newBinding->line = line;
-        newBinding->minTime = length;
-        newBinding->maxTime = length;
-        newBinding->level = nestingLevel;
-        newBinding->eventType = (QmlEventType)type;
-        newBinding->details = new QString(details);
-        newBinding->parentList = new QmlEventList();
-        newBinding->childrenList = new QmlEventList();
-        d->m_rootHash.insert(location, newBinding);
-    }
-
-    if (nestingLevel < d->m_lastLevel) {
-        // I'm the parent of the former
-        if (d->m_pendingEvents.contains(nestingLevel+1)) {
-            foreach (QmlEventData *child, d->m_pendingEvents[nestingLevel + 1]) {
-                if (!newBinding->childrenList->contains(child))
-                    newBinding->childrenList->append(child);
-                if (!child->parentList->contains(newBinding))
-                    child->parentList->append(newBinding);
-            }
-            d->m_pendingEvents[nestingLevel + 1].clear();
-        }
-
-    }
-
-    if (nestingLevel > 1 && !d->m_pendingEvents[nestingLevel].contains(newBinding)) {
-        // I'm not root... there will come a parent later
-        d->m_pendingEvents[nestingLevel].append(newBinding);
-    }
-
-    d->m_lastLevel = nestingLevel;
-}
-
-void QmlProfilerEventStatistics::complete()
-{
-    d->postProcess();
-    emit dataReady();
-}
-
-void QmlProfilerEventStatistics::QmlProfilerEventStatisticsPrivate::postProcess()
-{
-    double totalTime = 0;
-
-    foreach (QmlEventData *binding, m_rootHash.values()) {
-        if (binding->filename->isEmpty())
-            continue;
-        totalTime += binding->duration;
-    }
-
-    foreach (QmlEventData *binding, m_rootHash.values()) {
-        if (binding->filename->isEmpty())
-            continue;
-        binding->percentOfTime = binding->duration * 100.0 / totalTime;
-        binding->timePerCall = binding->calls > 0 ? double(binding->duration) / binding->calls : 0;
-    }
-}
-
-////////////////////////////////////////////////////////////////////////////////////
-
-QmlProfilerEventsView::QmlProfilerEventsView(QWidget *parent, QmlProfilerEventStatistics *model) :
+QmlProfilerEventsView::QmlProfilerEventsView(QWidget *parent, QmlProfilerEventList *model) :
     QTreeView(parent), d(new QmlProfilerEventsViewPrivate(this))
 {
     setObjectName("QmlProfilerEventsView");
@@ -281,7 +127,7 @@ QmlProfilerEventsView::~QmlProfilerEventsView()
     delete d->m_model;
 }
 
-void QmlProfilerEventsView::setEventStatisticsModel( QmlProfilerEventStatistics *model )
+void QmlProfilerEventsView::setEventStatisticsModel( QmlProfilerEventList *model )
 {
     if (d->m_eventStatistics)
         disconnect(d->m_eventStatistics,SIGNAL(dataReady()),this,SLOT(buildModel()));
@@ -414,7 +260,7 @@ void QmlProfilerEventsView::buildModel()
 {
     if (d->m_eventStatistics) {
         clear();
-        d->buildModelFromList( d->m_eventStatistics->getEventList(), d->m_model->invisibleRootItem() );
+        d->buildModelFromList( d->m_eventStatistics->getEventDescriptions(), d->m_model->invisibleRootItem() );
 
         bool hasBranches = d->m_fieldShown[Parents] || d->m_fieldShown[Children];
         setRootIsDecorated(hasBranches);
@@ -434,18 +280,18 @@ void QmlProfilerEventsView::buildModel()
     }
 }
 
-void QmlProfilerEventsView::QmlProfilerEventsViewPrivate::buildModelFromList( const QmlEventList &list, QStandardItem *parentItem, const QmlEventList &visitedFunctionsList )
+void QmlProfilerEventsView::QmlProfilerEventsViewPrivate::buildModelFromList( const QmlEventDescriptions &list, QStandardItem *parentItem, const QmlEventDescriptions &visitedFunctionsList )
 {
     foreach (QmlEventData *binding, list) {
         if (visitedFunctionsList.contains(binding))
             continue;
 
-        if ((!m_showAnonymous) && binding->filename->isEmpty())
+        if ((!m_showAnonymous) && binding->filename.isEmpty())
             continue;
 
         QList<QStandardItem *> newRow;
         if (m_fieldShown[Name]) {
-            newRow << new EventsViewItem(*binding->displayname);
+            newRow << new EventsViewItem(binding->displayname);
         }
 
         if (m_fieldShown[Type]) {
@@ -459,8 +305,8 @@ void QmlProfilerEventsView::QmlProfilerEventsViewPrivate::buildModelFromList( co
         }
 
         if (m_fieldShown[TotalDuration]) {
-            newRow << new EventsViewItem(displayTime(binding->duration));
-            newRow.last()->setData(QVariant(binding->duration));
+            newRow << new EventsViewItem(displayTime(binding->cumulatedDuration));
+            newRow.last()->setData(QVariant(binding->cumulatedDuration));
         }
 
         if (m_fieldShown[CallCount]) {
@@ -484,8 +330,8 @@ void QmlProfilerEventsView::QmlProfilerEventsViewPrivate::buildModelFromList( co
         }
 
         if (m_fieldShown[Details]) {
-            newRow << new EventsViewItem(*binding->details);
-            newRow.last()->setData(QVariant(*binding->details));
+            newRow << new EventsViewItem(binding->details);
+            newRow.last()->setData(QVariant(binding->details));
         }
 
         if (!newRow.isEmpty()) {
@@ -494,25 +340,25 @@ void QmlProfilerEventsView::QmlProfilerEventsViewPrivate::buildModelFromList( co
                 item->setEditable(false);
 
             // metadata
-            newRow.at(0)->setData(QVariant(*binding->location),LocationRole);
-            newRow.at(0)->setData(QVariant(*binding->filename),FilenameRole);
+            newRow.at(0)->setData(QVariant(binding->location),LocationRole);
+            newRow.at(0)->setData(QVariant(binding->filename),FilenameRole);
             newRow.at(0)->setData(QVariant(binding->line),LineRole);
 
             // append
             parentItem->appendRow(newRow);
 
-            if (m_fieldShown[Parents] && !binding->parentList->isEmpty()) {
-                QmlEventList newParentList(visitedFunctionsList);
+            if (m_fieldShown[Parents] && !binding->parentList.isEmpty()) {
+                QmlEventDescriptions newParentList(visitedFunctionsList);
                 newParentList.append(binding);
 
-                buildModelFromList(*binding->parentList, newRow.at(0), newParentList);
+                buildModelFromList(binding->parentList, newRow.at(0), newParentList);
             }
 
-            if (m_fieldShown[Children] && !binding->childrenList->isEmpty()) {
-                QmlEventList newChildrenList(visitedFunctionsList);
+            if (m_fieldShown[Children] && !binding->childrenList.isEmpty()) {
+                QmlEventDescriptions newChildrenList(visitedFunctionsList);
                 newChildrenList.append(binding);
 
-                buildModelFromList(*binding->childrenList, newRow.at(0), newChildrenList);
+                buildModelFromList(binding->childrenList, newRow.at(0), newChildrenList);
             }
         }
     }
@@ -556,5 +402,10 @@ void QmlProfilerEventsView::jumpToItem(const QModelIndex &index)
     emit gotoSourceLocation(fileName, line);
 }
 
+void QmlProfilerEventsView::contextMenuEvent(QContextMenuEvent *ev)
+{
+    emit contextMenuRequested(ev->globalPos());
+}
+
 } // namespace Internal
 } // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.h b/src/plugins/qmlprofiler/qmlprofilereventview.h
index a04b6d3128a..60f5b13acc7 100644
--- a/src/plugins/qmlprofiler/qmlprofilereventview.h
+++ b/src/plugins/qmlprofiler/qmlprofilereventview.h
@@ -35,39 +35,11 @@
 
 #include <QTreeView>
 #include <qmljsdebugclient/qmlprofilereventtypes.h>
+#include "qmlprofilereventlist.h"
 
 namespace QmlProfiler {
 namespace Internal {
 
-struct QmlEventData
-{
-    QmlEventData() : displayname(0) , filename(0) , location(0) , details(0),
-        line(0), eventType(QmlJsDebugClient::MaximumQmlEventType), level(-1), parentList(0), childrenList(0) {}
-    ~QmlEventData() {
-        delete displayname;
-        delete filename;
-        delete location;
-        delete parentList;
-        delete childrenList;
-    }
-    QString *displayname;
-    QString *filename;
-    QString *location;
-    QString *details;
-    int line;
-    QmlJsDebugClient::QmlEventType eventType;
-    qint64 level;
-    QList< QmlEventData *> *parentList;
-    QList< QmlEventData *> *childrenList;
-    qint64 duration;
-    qint64 calls;
-    qint64 minTime;
-    qint64 maxTime;
-    double timePerCall;
-    double percentOfTime;
-};
-
-
 typedef QHash<QString, QmlEventData *> QmlEventHash;
 typedef QList<QmlEventData *> QmlEventList;
 
@@ -77,31 +49,6 @@ enum ItemRole {
     LineRole = Qt::UserRole+3
 };
 
-class QmlProfilerEventStatistics : public QObject
-{
-    Q_OBJECT
-public:
-
-    explicit QmlProfilerEventStatistics(QObject *parent = 0);
-    ~QmlProfilerEventStatistics();
-
-    QmlEventList getEventList() const;
-    int eventCount() const;
-
-signals:
-    void dataReady();
-
-public slots:
-    void clear();
-    void addRangedEvent(int type, int nestingLevel, int nestingInType, qint64 startTime, qint64 length,
-                        const QStringList &data, const QString &fileName, int line);
-    void complete();
-
-private:
-    class QmlProfilerEventStatisticsPrivate;
-    QmlProfilerEventStatisticsPrivate *d;
-};
-
 class QmlProfilerEventsView : public QTreeView
 {
     Q_OBJECT
@@ -130,16 +77,17 @@ public:
         MaxViewTypes
     };
 
-    explicit QmlProfilerEventsView(QWidget *parent, QmlProfilerEventStatistics *model);
+    explicit QmlProfilerEventsView(QWidget *parent, QmlProfilerEventList *model);
     ~QmlProfilerEventsView();
 
-    void setEventStatisticsModel( QmlProfilerEventStatistics *model );
+    void setEventStatisticsModel( QmlProfilerEventList *model );
     void setFieldViewable(Fields field, bool show);
     void setViewType(ViewTypes type);
     void setShowAnonymousEvents( bool showThem );
 
 signals:
     void gotoSourceLocation(const QString &fileName, int lineNumber);
+    void contextMenuRequested(const QPoint &position);
 
 public slots:
     void clear();
@@ -148,6 +96,7 @@ public slots:
 
 private:
     void setHeaderLabels();
+    void contextMenuEvent(QContextMenuEvent *ev);
 
 private:
     class QmlProfilerEventsViewPrivate;
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp
index 855472c19dc..00738542f20 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp
@@ -35,6 +35,7 @@
 #include "qmlprofilerplugin.h"
 #include "qmlprofilerconstants.h"
 #include "qmlprofilerattachdialog.h"
+#include "qmlprofilereventlist.h"
 #include "qmlprofilereventview.h"
 
 #include "tracewindow.h"
@@ -76,6 +77,8 @@
 #include <QtGui/QToolButton>
 #include <QtGui/QMessageBox>
 #include <QtGui/QDockWidget>
+#include <QtGui/QFileDialog>
+#include <QtGui/QMenu>
 
 using namespace Analyzer;
 using namespace QmlProfiler::Internal;
@@ -93,7 +96,6 @@ public:
     QTimer m_connectionTimer;
     int m_connectionAttempts;
     TraceWindow *m_traceWindow;
-    QmlProfilerEventStatistics *m_statistics;
     QmlProfilerEventsView *m_eventsView;
     QmlProfilerEventsView *m_calleeView;
     QmlProfilerEventsView *m_callerView;
@@ -165,6 +167,19 @@ IAnalyzerTool::ToolMode QmlProfilerTool::toolMode() const
     return AnyMode;
 }
 
+void QmlProfilerTool::showContextMenu(const QPoint &position)
+{
+    QMenu menu;
+    QAction *loadAction = menu.addAction(tr("Load QML Trace"));
+    QAction *saveAction = menu.addAction(tr("Save QML Trace"));
+
+    QAction *selectedAction = menu.exec(position);
+    if (selectedAction == loadAction)
+        showLoadDialog();
+    if (selectedAction == saveAction)
+        showSaveDialog();
+}
+
 IAnalyzerEngine *QmlProfilerTool::createEngine(const AnalyzerStartParameters &sp,
     ProjectExplorer::RunConfiguration *runConfiguration)
 {
@@ -239,27 +254,26 @@ QWidget *QmlProfilerTool::createWidgets()
 
     connect(d->m_traceWindow, SIGNAL(gotoSourceLocation(QString,int)),this, SLOT(gotoSourceLocation(QString,int)));
     connect(d->m_traceWindow, SIGNAL(timeChanged(qreal)), this, SLOT(updateTimer(qreal)));
+    connect(d->m_traceWindow, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
 
-    d->m_statistics = new QmlProfilerEventStatistics(mw);
-    d->m_eventsView = new QmlProfilerEventsView(mw, d->m_statistics);
+    d->m_eventsView = new QmlProfilerEventsView(mw, d->m_traceWindow->getEventList());
     d->m_eventsView->setViewType(QmlProfilerEventsView::EventsView);
 
-    connect(d->m_traceWindow, SIGNAL(range(int,int,int,qint64,qint64,QStringList,QString,int)),
-            d->m_statistics, SLOT(addRangedEvent(int,int,int,qint64,qint64,QStringList,QString,int)));
-    connect(d->m_traceWindow, SIGNAL(viewUpdated()),
-            d->m_statistics, SLOT(complete()));
     connect(d->m_eventsView, SIGNAL(gotoSourceLocation(QString,int)),
             this, SLOT(gotoSourceLocation(QString,int)));
+    connect(d->m_eventsView, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
 
-    d->m_calleeView = new QmlProfilerEventsView(mw, d->m_statistics);
+    d->m_calleeView = new QmlProfilerEventsView(mw, d->m_traceWindow->getEventList());
     d->m_calleeView->setViewType(QmlProfilerEventsView::CalleesView);
     connect(d->m_calleeView, SIGNAL(gotoSourceLocation(QString,int)),
             this, SLOT(gotoSourceLocation(QString,int)));
+    connect(d->m_calleeView, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
 
-    d->m_callerView = new QmlProfilerEventsView(mw, d->m_statistics);
+    d->m_callerView = new QmlProfilerEventsView(mw, d->m_traceWindow->getEventList());
     d->m_callerView->setViewType(QmlProfilerEventsView::CallersView);
     connect(d->m_callerView, SIGNAL(gotoSourceLocation(QString,int)),
             this, SLOT(gotoSourceLocation(QString,int)));
+    connect(d->m_callerView, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
 
     QDockWidget *eventsDock = AnalyzerManager::createDockWidget
             (this, tr("Events"), d->m_eventsView, Qt::BottomDockWidgetArea);
@@ -403,7 +417,7 @@ void QmlProfilerTool::gotoSourceLocation(const QString &fileUrl, int lineNumber)
 }
 
 void QmlProfilerTool::correctTimer() {
-    if (d->m_statistics->eventCount() == 0)
+    if (d->m_traceWindow->getEventList()->count() == 0)
         updateTimer(0);
 }
 
@@ -423,7 +437,6 @@ void QmlProfilerTool::updateProjectFileList()
 void QmlProfilerTool::clearDisplay()
 {
     d->m_traceWindow->clearDisplay();
-    d->m_statistics->clear();
     d->m_eventsView->clear();
     d->m_calleeView->clear();
     d->m_callerView->clear();
@@ -546,3 +559,26 @@ void QmlProfilerTool::logError(const QString &msg)
     Core::MessageManager *messageManager = Core::MessageManager::instance();
     messageManager->printToOutputPane(msg, true);
 }
+
+void QmlProfilerTool::showSaveDialog()
+{
+    Core::ICore *core = Core::ICore::instance();
+    QString filename = QFileDialog::getSaveFileName(core->mainWindow(), tr("Save QML Trace"), QString(), tr("QML traces (*.xml)"));
+    if (!filename.isEmpty()) {
+        if (!filename.endsWith(QLatin1String(".xml")))
+            filename += QLatin1String(".xml");
+        d->m_traceWindow->getEventList()->save(filename);
+    }
+}
+
+void QmlProfilerTool::showLoadDialog()
+{
+    Core::ICore *core = Core::ICore::instance();
+    QString filename = QFileDialog::getOpenFileName(core->mainWindow(), tr("Load QML Trace"), QString(), tr("QML traces (*.xml)"));
+
+    if (!filename.isEmpty()) {
+        // delayed load (prevent graphical artifacts due to long load time)
+        d->m_traceWindow->getEventList()->setFilename(filename);
+        QTimer::singleShot(100, d->m_traceWindow->getEventList(), SLOT(load()));
+    }
+}
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.h b/src/plugins/qmlprofiler/qmlprofilertool.h
index 2c627e9c876..594f1465857 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.h
+++ b/src/plugins/qmlprofiler/qmlprofilertool.h
@@ -36,6 +36,8 @@
 #include <analyzerbase/ianalyzertool.h>
 #include <analyzerbase/ianalyzerengine.h>
 
+#include <QtCore/QPoint>
+
 namespace QmlProfiler {
 namespace Internal {
 
@@ -74,6 +76,8 @@ public slots:
 
     void clearDisplay();
 
+    void showContextMenu(const QPoint &position);
+
 signals:
     void setTimeLabel(const QString &);
     void fetchingData(bool);
@@ -84,6 +88,8 @@ private slots:
     void attach();
     void tryToConnect();
     void connectionStateChanged();
+    void showSaveDialog();
+    void showLoadDialog();
 
 private:
     void connectToClient();
diff --git a/src/plugins/qmlprofiler/timelineview.cpp b/src/plugins/qmlprofiler/timelineview.cpp
index 930672a8471..7e141f35683 100644
--- a/src/plugins/qmlprofiler/timelineview.cpp
+++ b/src/plugins/qmlprofiler/timelineview.cpp
@@ -34,12 +34,17 @@
 
 #include <qdeclarativecontext.h>
 #include <qdeclarativeproperty.h>
+#include <QtCore/QTimer>
 
 using namespace QmlProfiler::Internal;
 
+#define CACHE_ENABLED true
+#define CACHE_UPDATEDELAY 10
+#define CACHE_STEP 200
+
 TimelineView::TimelineView(QDeclarativeItem *parent) :
-    QDeclarativeItem(parent), m_delegate(0), m_startTime(0), m_endTime(0), m_startX(0),
-    prevMin(0), prevMax(0), m_totalWidth(0)
+    QDeclarativeItem(parent), m_delegate(0), m_itemCount(0), m_startTime(0), m_endTime(0), m_startX(0), m_spacing(0),
+    prevMin(0), prevMax(0), m_eventList(0), m_totalWidth(0), m_lastCachedIndex(0), m_creatingCache(false), m_oldCacheSize(0)
 {
 }
 
@@ -50,46 +55,20 @@ void TimelineView::componentComplete()
 
 void TimelineView::clearData()
 {
-    m_rangeList.clear();
-    m_items.clear();
+    if (CACHE_ENABLED)
+        foreach (QDeclarativeItem *item, m_items.values())
+            item->setVisible(false);
+    else
+        foreach (QDeclarativeItem *item, m_items.values())
+            delete m_items.take(m_items.key(item));
+
     m_startTime = 0;
     m_endTime = 0;
     m_startX = 0;
     prevMin = 0;
     prevMax = 0;
-    m_prevLimits.clear();
     m_totalWidth = 0;
-
-}
-
-void TimelineView::setRanges(const QScriptValue &value)
-{
-    //TODO clear old values (always?)
-    m_ranges = value;
-
-    //### below code not yet used anywhere
-    int length = m_ranges.property("length").toInt32();
-
-    for (int i = 0; i < length; ++i) {
-        int type = m_ranges.property(i).property("type").toNumber();
-        Q_ASSERT(type >= 0);
-        while (m_rangeList.count() <= type)
-            m_rangeList.append(ValueList());
-        m_rangeList[type] << m_ranges.property(i);
-    }
-
-    for (int i = 0; i < m_rangeList.count(); ++i)
-        m_prevLimits << PrevLimits(0, 0);
-
-    qreal startValue = m_ranges.property(0).property("start").toNumber();
-    m_starts.clear();
-    m_starts.reserve(length);
-    m_ends.clear();
-    m_ends.reserve(length);
-    for (int i = 0; i < length; ++i) {
-        m_starts.append(m_ranges.property(i).property("start").toNumber() - startValue);
-        m_ends.append(m_ranges.property(i).property("start").toNumber() + m_ranges.property(i).property("duration").toNumber() - startValue);
-    }
+    m_lastCachedIndex = 0;
 }
 
 void TimelineView::setStartX(qreal arg)
@@ -97,9 +76,6 @@ void TimelineView::setStartX(qreal arg)
     if (arg == m_startX)
         return;
 
-    if (!m_ranges.isArray())
-        return;
-
     qreal window = m_endTime - m_startTime;
     if (window == 0)    //###
         return;
@@ -118,51 +94,29 @@ void TimelineView::updateTimeline(bool updateStartX)
     if (!m_delegate)
         return;
 
-    if (!m_ranges.isArray())
+    if (!m_eventList)
         return;
 
-    int length = m_ranges.property("length").toInt32();
-
-    qreal startValue = m_ranges.property(0).property("start").toNumber();
-    qreal endValue = m_ranges.property(length-1).property("start").toNumber() + m_ranges.property(length-1).property("duration").toNumber();
-
-    qreal totalRange = endValue - startValue;
+    qreal totalRange = m_eventList->lastTimeMark() - m_eventList->firstTimeMark();
     qreal window = m_endTime - m_startTime;
 
     if (window == 0)    //###
         return;
 
-    qreal spacing = width() / window;
-    qreal oldtw = m_totalWidth;
-    m_totalWidth = totalRange * spacing;
-
-    // Find region samples
-    int minsample = 0;
-    int maxsample = 0;
+    qreal newSpacing = width() / window;
+    bool spacingChanged = (newSpacing != m_spacing);
+    m_spacing = newSpacing;
 
-    for (int i = 0; i < length; ++i) {
-        if (m_ends.at(i) >= m_startTime)
-            break;
-        minsample = i;
-    }
-
-    for (int i = minsample + 1; i < length; ++i) {
-        maxsample = i;
-        if (m_starts.at(i) > m_endTime)
-            break;
-    }
+    qreal oldtw = m_totalWidth;
+    m_totalWidth = totalRange * m_spacing;
 
-    //### overkill (if we can expose whether or not data is nested)
-    for (int i = maxsample + 1; i < length; ++i) {
-        if (m_starts.at(i) < m_endTime)
-            maxsample = i;
-    }
 
-    //qDebug() << maxsample - minsample;
+    int minsample = m_eventList->findFirstIndex(m_startTime + m_eventList->firstTimeMark());
+    int maxsample = m_eventList->findLastIndex(m_endTime + m_eventList->firstTimeMark());
 
     if (updateStartX) {
         qreal oldStartX = m_startX;
-        m_startX = qRound(m_startTime * spacing);
+        m_startX = qRound(m_startTime * m_spacing);
         if (m_startX != oldStartX) {
             emit startXChanged(m_startX);
         }
@@ -172,69 +126,181 @@ void TimelineView::updateTimeline(bool updateStartX)
     if (m_totalWidth != oldtw)
         emit totalWidthChanged(m_totalWidth);
 
-    //clear items no longer in view
-    while (prevMin < minsample) {
-        delete m_items.take(prevMin);
-        ++prevMin;
-    }
-    while (prevMax > maxsample) {
-        delete m_items.take(prevMax);
-        --prevMax;
-    }
 
-    // Show items
-    int z = 0;
-    for (int i = maxsample; i >= minsample; --i) {
-        QDeclarativeItem *item = 0;
-        item = m_items.value(i);
-        bool creating = false;
-        if (!item) {
-            QDeclarativeContext *ctxt = new QDeclarativeContext(qmlContext(this));
-            item = qobject_cast<QDeclarativeItem*>(m_delegate->beginCreate(ctxt));
-            m_items.insert(i, item);
-            creating = true;
-
-            int type = m_ranges.property(i).property("type").toNumber();
-
-            ctxt->setParent(item); //### QDeclarative_setParent_noEvent(ctxt, item); instead?
-            ctxt->setContextProperty("duration", qMax(qRound(m_ranges.property(i).property("duration").toNumber()/qreal(1000)),1));
-            ctxt->setContextProperty("fileName", m_ranges.property(i).property("fileName").toString());
-            ctxt->setContextProperty("line", m_ranges.property(i).property("line").toNumber());
-            ctxt->setContextProperty("index", i);
-            ctxt->setContextProperty("nestingLevel", m_ranges.property(i).property("nestingLevel").toNumber());
-            ctxt->setContextProperty("nestingDepth", m_ranges.property(i).property("nestingDepth").toNumber());
-            QString label;
-            QVariantList list = m_ranges.property(i).property("label").toVariant().value<QVariantList>();
-            for (int i = 0; i < list.size(); ++i) {
-                if (i > 0)
-                    label += QLatin1Char('\n');
-                QString sub = list.at(i).toString();
-
-                //### only do rewrite for bindings...
-                if (type == 3) {
-                    //### don't construct in loop
-                    QRegExp rewrite("\\(function \\$(\\w+)\\(\\) \\{ return (.+) \\}\\)");
-                    bool match = rewrite.exactMatch(sub);
-                    if (match)
-                        sub = rewrite.cap(1) + ": " + rewrite.cap(2);
-                }
+    // the next loops have to be modified with the new implementation of the cache
 
-                label += sub;
+    // hide items that are not visible any more
+    if (maxsample < prevMin || minsample > prevMax) {
+        for (int i = prevMin; i <= prevMax; ++i)
+            if (m_items.contains(i)) {
+                if (CACHE_ENABLED)
+                    m_items.value(i)->setVisible(false);
+                else
+                    delete m_items.take(i);
             }
-            ctxt->setContextProperty("label", label);
-            ctxt->setContextProperty("type", type);
-            item->setParentItem(this);
+    } else {
+        if (minsample > prevMin && minsample <= prevMax)
+            for (int i = prevMin; i < minsample; ++i)
+                if (m_items.contains(i)) {
+                    if (CACHE_ENABLED)
+                        m_items.value(i)->setVisible(false);
+                    else
+                        delete m_items.take(i);
+                }
+
+        if (maxsample >= prevMin && maxsample < prevMax)
+            for (int i = maxsample + 1; i <= prevMax; ++i)
+                if (m_items.contains(i)) {
+                    if (CACHE_ENABLED)
+                        m_items.value(i)->setVisible(false);
+                    else
+                        delete m_items.take(i);
+                }
+    }
+
+    // Update visible items
+    for (int i = minsample; i <= maxsample; ++i) {
+        if (!m_items.contains(i)) {
+            createItem(i);
+            m_items.value(i)->setVisible(true);
         }
-        if (item) {
-            item->setX(m_starts.at(i)*spacing);
-            qreal width = (m_ends.at(i)-m_starts.at(i)) * spacing;
-            item->setWidth(width > 1 ? width : 1);
-            item->setZValue(++z);
+        else
+        if (spacingChanged || !m_items.value(i)->isVisible()) {
+            m_items.value(i)->setVisible(true);
+            updateItemPosition(i);
         }
-        if (creating)
-            m_delegate->completeCreate();
     }
 
     prevMin = minsample;
     prevMax = maxsample;
+
+}
+
+void TimelineView::createItem(int itemIndex)
+{
+    QDeclarativeContext *ctxt = new QDeclarativeContext(qmlContext(this));
+    QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(m_delegate->beginCreate(ctxt));
+    m_items.insert(itemIndex, item);
+
+    ctxt->setParent(item); //### QDeclarative_setParent_noEvent(ctxt, item); instead?
+    ctxt->setContextProperty("index", itemIndex);
+    ctxt->setContextProperty("type", m_eventList->getType(itemIndex));
+    ctxt->setContextProperty("nestingLevel", m_eventList->getNestingLevel(itemIndex));
+    ctxt->setContextProperty("nestingDepth", m_eventList->getNestingDepth(itemIndex));
+
+    updateItemPosition(itemIndex);
+
+    item->setVisible(false);
+
+    item->setParentItem(this);
+    m_delegate->completeCreate();
+    m_itemCount++;
+}
+
+void TimelineView::updateItemPosition(int itemIndex)
+{
+    QDeclarativeItem *item = m_items.value(itemIndex);
+    if (item) {
+        qreal itemStartPos = (m_eventList->getStartTime(itemIndex) - m_eventList->firstTimeMark()) * m_spacing;
+        item->setX(itemStartPos);
+        qreal width = (m_eventList->getEndTime(itemIndex) - m_eventList->getStartTime(itemIndex)) * m_spacing;
+        item->setWidth(width > 1 ? width : 1);
+    }
+}
+
+void TimelineView::rebuildCache()
+{
+    if (CACHE_ENABLED) {
+        m_lastCachedIndex = 0;
+        m_creatingCache = false;
+        m_oldCacheSize = m_items.count();
+        emit cachedProgressChanged();
+        QTimer::singleShot(CACHE_UPDATEDELAY, this, SLOT(purgeCache()));
+    } else {
+        m_creatingCache = true;
+        m_lastCachedIndex = m_eventList->count();
+        emit cacheReady();
+    }
+}
+
+qreal TimelineView::cachedProgress() const
+{
+    qreal progress;
+    if (!m_creatingCache) {
+        if (m_oldCacheSize == 0)
+            progress = 0.5;
+       else
+            progress = (m_lastCachedIndex * 0.5) / m_oldCacheSize;
+    }
+    else
+        progress = 0.5 + (m_lastCachedIndex * 0.5) / m_eventList->count();
+
+    return progress;
+}
+
+void TimelineView::increaseCache()
+{
+    int totalCount = m_eventList->count();
+    if (m_lastCachedIndex >= totalCount) {
+        emit cacheReady();
+        return;
+    }
+
+    for (int i = 0; i < CACHE_STEP; i++) {
+        createItem(m_lastCachedIndex);
+        m_lastCachedIndex++;
+        if (m_lastCachedIndex >= totalCount)
+            break;
+    }
+
+    emit cachedProgressChanged();
+
+    QTimer::singleShot(CACHE_UPDATEDELAY, this, SLOT(increaseCache()));
+}
+
+void TimelineView::purgeCache()
+{
+    if (m_items.isEmpty()) {
+        m_creatingCache = true;
+        m_lastCachedIndex = 0;
+        QTimer::singleShot(CACHE_UPDATEDELAY, this, SLOT(increaseCache()));
+        return;
+    }
+
+    for (int i=0; i < CACHE_STEP; i++)
+    {
+        if (m_items.contains(m_lastCachedIndex))
+            delete m_items.take(m_lastCachedIndex);
+
+        m_lastCachedIndex++;
+        if (m_items.isEmpty())
+            break;
+    }
+
+    emit cachedProgressChanged();
+    QTimer::singleShot(CACHE_UPDATEDELAY, this, SLOT(purgeCache()));
+}
+
+qint64 TimelineView::getDuration(int index) const
+{
+    Q_ASSERT(m_eventList);
+    return m_eventList->getEndTime(index) - m_eventList->getStartTime(index);
+}
+
+QString TimelineView::getFilename(int index) const
+{
+    Q_ASSERT(m_eventList);
+    return m_eventList->getFilename(index);
+}
+
+int TimelineView::getLine(int index) const
+{
+    Q_ASSERT(m_eventList);
+    return m_eventList->getLine(index);
+}
+
+QString TimelineView::getDetails(int index) const
+{
+    Q_ASSERT(m_eventList);
+    return m_eventList->getDetails(index);
 }
diff --git a/src/plugins/qmlprofiler/timelineview.h b/src/plugins/qmlprofiler/timelineview.h
index ca0bffc7925..8bf35ee6966 100644
--- a/src/plugins/qmlprofiler/timelineview.h
+++ b/src/plugins/qmlprofiler/timelineview.h
@@ -35,6 +35,7 @@
 
 #include <QtDeclarative/QDeclarativeItem>
 #include <QtScript/QScriptValue>
+#include <qmlprofilereventlist.h>
 
 namespace QmlProfiler {
 namespace Internal {
@@ -47,6 +48,8 @@ class TimelineView : public QDeclarativeItem
     Q_PROPERTY(qint64 endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
     Q_PROPERTY(qreal startX READ startX WRITE setStartX NOTIFY startXChanged)
     Q_PROPERTY(qreal totalWidth READ totalWidth NOTIFY totalWidthChanged)
+    Q_PROPERTY(QObject* eventList READ eventList WRITE setEventList NOTIFY eventListChanged)
+    Q_PROPERTY(qreal cachedProgress READ cachedProgress NOTIFY cachedProgressChanged)
 
 public:
     explicit TimelineView(QDeclarativeItem *parent = 0);
@@ -76,16 +79,34 @@ public:
         return m_totalWidth;
     }
 
+    qreal cachedProgress() const;
+
+    QmlProfilerEventList *eventList() const { return m_eventList; }
+    void setEventList(QObject *eventList)
+    {
+        m_eventList = qobject_cast<QmlProfilerEventList *>(eventList);
+        emit eventListChanged(m_eventList);
+    }
+
+    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 void rebuildCache();
+
 signals:
     void delegateChanged(QDeclarativeComponent * arg);
     void startTimeChanged(qint64 arg);
     void endTimeChanged(qint64 arg);
     void startXChanged(qreal arg);
     void totalWidthChanged(qreal arg);
+    void eventListChanged(QmlProfilerEventList *list);
+
+    void cachedProgressChanged();
+    void cacheReady();
 
 public slots:
     void clearData();
-    void setRanges(const QScriptValue &value);
     void updateTimeline(bool updateStartX = true);
 
     void setDelegate(QDeclarativeComponent * arg)
@@ -117,28 +138,32 @@ public slots:
 protected:
     void componentComplete();
 
+private:
+    void createItem(int itemIndex);
+    void updateItemPosition(int itemIndex);
+
+public slots:
+    void increaseCache();
+    void purgeCache();
+
 private:
     QDeclarativeComponent * m_delegate;
-    QScriptValue m_ranges;
-    typedef QList<QScriptValue> ValueList;
-    QList<ValueList> m_rangeList;
     QHash<int,QDeclarativeItem*> m_items;
+    qint64 m_itemCount;
     qint64 m_startTime;
     qint64 m_endTime;
     qreal m_startX;
+    qreal m_spacing;
     int prevMin;
     int prevMax;
-    QList<qreal> m_starts;
-    QList<qreal> m_ends;
 
-    struct PrevLimits {
-        PrevLimits(int _min, int _max) : min(_min), max(_max) {}
-        int min;
-        int max;
-    };
+    QmlProfilerEventList *m_eventList;
 
-    QList<PrevLimits> m_prevLimits;
     qreal m_totalWidth;
+    int m_lastCachedIndex;
+    bool m_creatingCache;
+    int m_oldCacheSize;
+
 };
 
 } // namespace Internal
diff --git a/src/plugins/qmlprofiler/tracewindow.cpp b/src/plugins/qmlprofiler/tracewindow.cpp
index b54115e0f98..48403cf6491 100644
--- a/src/plugins/qmlprofiler/tracewindow.cpp
+++ b/src/plugins/qmlprofiler/tracewindow.cpp
@@ -33,6 +33,7 @@
 #include "tracewindow.h"
 
 #include "qmlprofilerplugin.h"
+#include "qmlprofilereventlist.h"
 
 #include <qmljsdebugclient/qdeclarativedebugclient.h>
 #include <qmljsdebugclient/qmlprofilertraceclient.h>
@@ -43,6 +44,7 @@
 #include <QtGui/QVBoxLayout>
 #include <QtGui/QToolButton>
 #include <QtGui/QGraphicsObject>
+#include <QtGui/QContextMenuEvent>
 
 using namespace QmlJsDebugClient;
 
@@ -97,14 +99,17 @@ TraceWindow::TraceWindow(QWidget *parent)
     toolBarLayout->addWidget(buttonZoomIn);
     toolBarLayout->addWidget(buttonZoomOut);
 
-
-
     m_view->setResizeMode(QDeclarativeView::SizeRootObjectToView);
     m_view->setFocus();
     groupLayout->addWidget(m_view);
 
     setLayout(groupLayout);
 
+    m_eventList = new QmlProfilerEventList(this);
+    connect(this,SIGNAL(range(int,qint64,qint64,QStringList,QString,int)), m_eventList, SLOT(addRangedEvent(int,qint64,qint64,QStringList,QString,int)));
+    connect(this,SIGNAL(viewUpdated()), m_eventList, SLOT(complete()));
+    m_view->rootContext()->setContextProperty("qmlEventList", m_eventList);
+
     // Minimum height: 5 rows of 20 pixels + scrollbar of 50 pixels + 20 pixels margin
     setMinimumHeight(170);
 }
@@ -121,8 +126,8 @@ void TraceWindow::reset(QDeclarativeDebugConnection *conn)
     delete m_plugin.data();
     m_plugin = new QmlProfilerTraceClient(conn);
     connect(m_plugin.data(), SIGNAL(complete()), this, SIGNAL(viewUpdated()));
-    connect(m_plugin.data(), SIGNAL(range(int,int,int,qint64,qint64,QStringList,QString,int)),
-            this, SIGNAL(range(int,int,int,qint64,qint64,QStringList,QString,int)));
+    connect(m_plugin.data(), SIGNAL(range(int,qint64,qint64,QStringList,QString,int)),
+            this, SIGNAL(range(int,qint64,qint64,QStringList,QString,int)));
 
     m_view->rootContext()->setContextProperty("connection", m_plugin.data());
     m_view->setSource(QUrl("qrc:/qmlprofiler/MainView.qml"));
@@ -131,7 +136,7 @@ void TraceWindow::reset(QDeclarativeDebugConnection *conn)
 
     connect(m_view->rootObject(), SIGNAL(updateCursorPosition()), this, SLOT(updateCursorPosition()));
     connect(m_view->rootObject(), SIGNAL(updateTimer()), this, SLOT(updateTimer()));
-    connect(m_view->rootObject(), SIGNAL(dataAvailableChanged()), this, SLOT(updateToolbar()));
+    connect(m_eventList, SIGNAL(countChanged()), this, SLOT(updateToolbar()));
     connect(this, SIGNAL(jumpToPrev()), m_view->rootObject(), SLOT(prevEvent()));
     connect(this, SIGNAL(jumpToNext()), m_view->rootObject(), SLOT(nextEvent()));
     connect(this, SIGNAL(zoomIn()), m_view->rootObject(), SLOT(zoomIn()));
@@ -140,6 +145,16 @@ void TraceWindow::reset(QDeclarativeDebugConnection *conn)
     connect(this, SIGNAL(internalClearDisplay()), m_view->rootObject(), SLOT(clearAll()));
 }
 
+QmlProfilerEventList *TraceWindow::getEventList() const
+{
+    return m_eventList;
+}
+
+void TraceWindow::contextMenuEvent(QContextMenuEvent *ev)
+{
+    emit contextMenuRequested(ev->globalPos());
+}
+
 void TraceWindow::updateCursorPosition()
 {
     emit gotoSourceLocation(m_view->rootObject()->property("fileName").toString(),
@@ -153,17 +168,17 @@ void TraceWindow::updateTimer()
 
 void TraceWindow::clearDisplay()
 {
+    m_eventList->clear();
+
     if (m_plugin)
         m_plugin.data()->clearData();
-    else
-        emit internalClearDisplay();
+
+    emit internalClearDisplay();
 }
 
 void TraceWindow::updateToolbar()
 {
-    bool dataAvailable = m_view->rootObject()->property("dataAvailable").toBool() &&
-            m_view->rootObject()->property("eventCount").toInt() > 0;
-    emit enableToolbar(dataAvailable);
+    emit enableToolbar(m_eventList && m_eventList->count()>0);
 }
 
 void TraceWindow::setRecording(bool recording)
diff --git a/src/plugins/qmlprofiler/tracewindow.h b/src/plugins/qmlprofiler/tracewindow.h
index 5629f977f04..04bbcab2613 100644
--- a/src/plugins/qmlprofiler/tracewindow.h
+++ b/src/plugins/qmlprofiler/tracewindow.h
@@ -34,6 +34,7 @@
 #define TRACEWINDOW_H
 
 #include <qmljsdebugclient/qmlprofilertraceclient.h>
+#include "qmlprofilereventlist.h"
 
 #include <QtCore/QPointer>
 #include <QtGui/QWidget>
@@ -55,6 +56,8 @@ public:
 
     void reset(QmlJsDebugClient::QDeclarativeDebugConnection *conn);
 
+    QmlProfilerEventList *getEventList() const;
+
     void setRecording(bool recording);
     bool isRecording() const;
 
@@ -69,7 +72,7 @@ signals:
     void viewUpdated();
     void gotoSourceLocation(const QString &fileUrl, int lineNumber);
     void timeChanged(qreal newTime);
-    void range(int type, int nestingLevel, int nestingInType, qint64 startTime, qint64 length, const QStringList &data, const QString &fileName, int line);
+    void range(int type, qint64 startTime, qint64 length, const QStringList &data, const QString &fileName, int line);
 
     void internalClearDisplay();
     void jumpToPrev();
@@ -78,11 +81,17 @@ signals:
     void zoomOut();
     void enableToolbar(bool);
 
+    void contextMenuRequested(const QPoint& position);
+
+private:
+    void contextMenuEvent(QContextMenuEvent *);
+
 private:
     QWeakPointer<QmlJsDebugClient::QmlProfilerTraceClient> m_plugin;
     QSize m_sizeHint;
 
     QDeclarativeView *m_view;
+    QmlProfilerEventList *m_eventList;
 };
 
 } // namespace Internal
-- 
GitLab