diff --git a/src/plugins/analyzerbase/analyzermanager.cpp b/src/plugins/analyzerbase/analyzermanager.cpp index ee872b9950d..ab4eb527f1d 100644 --- a/src/plugins/analyzerbase/analyzermanager.cpp +++ b/src/plugins/analyzerbase/analyzermanager.cpp @@ -101,7 +101,7 @@ using namespace Analyzer::Internal; // A separate 'Analzye' mode is not used in Qt Creator 2.2. // Consider re-introducing it if a real use case for a separate main window with docks // appears. -enum { useAnalyzeMode = 0 }; +enum { useAnalyzeMode = 1 }; namespace Analyzer { namespace Internal { diff --git a/src/plugins/analyzerbase/analyzerruncontrol.cpp b/src/plugins/analyzerbase/analyzerruncontrol.cpp index e853b67570c..e46aac0b617 100644 --- a/src/plugins/analyzerbase/analyzerruncontrol.cpp +++ b/src/plugins/analyzerbase/analyzerruncontrol.cpp @@ -131,6 +131,7 @@ AnalyzerRunControl::~AnalyzerRunControl() { if (m_isRunning) stop(); + delete m_engine; } void AnalyzerRunControl::start() diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 209562857c0..62495c55b23 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -38,6 +38,7 @@ SUBDIRS = plugin_coreplugin \ plugin_classview \ plugin_tasklist \ plugin_analyzerbase \ + plugin_qmlprofiler \ plugin_qmljstools \ plugin_macros \ debugger/dumper.pro @@ -263,6 +264,10 @@ plugin_analyzerbase.depends += plugin_projectexplorer plugin_memcheck.depends += plugin_valgrindtoolbase } +plugin_qmlprofiler.subdir = qmlprofiler +plugin_qmlprofiler.depends = plugin_coreplugin +plugin_qmlprofiler.depends = plugin_analyzerbase + plugin_qmljstools.subdir = qmljstools plugin_qmljstools.depends = plugin_projectexplorer plugin_qmljstools.depends += plugin_coreplugin diff --git a/src/plugins/qmlprofiler/Detail.qml b/src/plugins/qmlprofiler/Detail.qml new file mode 100644 index 00000000000..cc7e852ade1 --- /dev/null +++ b/src/plugins/qmlprofiler/Detail.qml @@ -0,0 +1,34 @@ +import QtQuick 1.1 +import Monitor 1.0 +import "MainView.js" as Plotter + +Item { + id: detail + property string label + property string content + property int maxLines: 4 + signal linkActivated(string url) + + height: childrenRect.height + width: childrenRect.width + Item { + id: guideline + x: 70 + width: 5 + } + Text { + id: lbl + text: label + ":" + font.pixelSize: 12 + font.bold: true + anchors.right: guideline.left + } + Text { + text: content + font.pixelSize: 12 + anchors.baseline: lbl.baseline + anchors.left: guideline.right + maximumLineCount: maxLines + onLinkActivated: detail.linkActivated(link) + } +} diff --git a/src/plugins/qmlprofiler/Elapsed.qml b/src/plugins/qmlprofiler/Elapsed.qml new file mode 100644 index 00000000000..298782e120b --- /dev/null +++ b/src/plugins/qmlprofiler/Elapsed.qml @@ -0,0 +1,26 @@ +import QtQuick 1.1 +import Monitor 1.0 +import "MainView.js" as Plotter + +Text { + id: elapsed + color: "white" + + Timer { + property date startDate + property bool reset: true + running: connection.recording + repeat: true + onRunningChanged: if (running) reset = true + interval: 100 + triggeredOnStart: true + onTriggered: { + if (reset) { + startDate = new Date() + reset = false + } + var time = (new Date() - startDate)/1000 + elapsed.text = time.toFixed(1) + "s" + } + } +} diff --git a/src/plugins/qmlprofiler/Label.qml b/src/plugins/qmlprofiler/Label.qml new file mode 100644 index 00000000000..0363fc2bf88 --- /dev/null +++ b/src/plugins/qmlprofiler/Label.qml @@ -0,0 +1,23 @@ +import QtQuick 1.1 + +Item { + property alias text: txt.text + + height: 50 + width: 150 //### required, or ignored by positioner + + Text { + id: txt; + x: 5 + font.pixelSize: 12 + color: "#232323" + anchors.verticalCenter: parent.verticalCenter + } + + Rectangle { + height: 1 + width: parent.width + color: "#cccccc" + anchors.bottom: parent.bottom + } +} diff --git a/src/plugins/qmlprofiler/MainView.js b/src/plugins/qmlprofiler/MainView.js new file mode 100644 index 00000000000..fe5df33009e --- /dev/null +++ b/src/plugins/qmlprofiler/MainView.js @@ -0,0 +1,110 @@ +.pragma library + +var values = [ ]; //events +var ranges = [ ]; +var frameFps = [ ]; +var valuesdone = false; +var xmargin = 0; +var ymargin = 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. +var colors = [ "#99CCB3", "#99CCCC", "#99B3CC", "#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ]; +var origColors = [ "#99CCB3", "#99CCCC", "#99B3CC", "#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ]; +var xRayColors = [ "#6699CCB3", "#6699CCCC", "#6699B3CC", "#669999CC", "#66CC99B3", "#66CC99CC", "#66CCCC99", "#66CCB399" ]; + +function reset() +{ + values = []; + ranges = []; + frameFps = []; + xmargin = 0; + ymargin = 0; + valuesdone = false; +} + +function calcFps() +{ + if (values.length) + frameFps = new Array(values.length - 1); + for (var i = 0; i < values.length - 1; ++i) { + var frameTime = (values[i + 1] - values[i]) / 1000000; + frameFps[i] = 1000 / frameTime; + } +} + +//draw background of the graph +function drawGraph(canvas, ctxt, region) +{ + var grad = ctxt.createLinearGradient(0, 0, 0, canvas.canvasSize.height); + grad.addColorStop(0, '#fff'); + grad.addColorStop(1, '#ccc'); + ctxt.fillStyle = grad; + + ctxt.fillRect(0, 0, canvas.canvasSize.width + xmargin, canvas.canvasSize.height - ymargin); +} + +//draw the actual data to be graphed +function drawData(canvas, ctxt, region) +{ + if (values.length == 0 && ranges.length == 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 spacing = width / sumValue; + + //### only draw those in range + for (var ii = 0; ii < ranges.length; ++ii) { + + var xx = (ranges[ii].start - ranges[0].start) * spacing + xmargin; + if (xx > region.x + region.width) + continue; + + var size = ranges[ii].duration * spacing; + if (xx + size < region.x) + continue; + + if (size < 0.5) + size = 0.5; + + ctxt.fillStyle = "rgba(0,0,0,1)" //colors[ranges[ii].type]; + ctxt.fillRect(xx, ranges[ii].type*10, size, 10); + } + + //draw fps overlay + var heightScale = height / 60; + ctxt.beginPath(); + ctxt.moveTo(0,0); + for (var i = 1; i < values.length; ++i) { + var xx = (values[i] - ranges[0].start) * spacing + xmargin; + ctxt.lineTo(xx, height - frameFps[i-1]*heightScale) + } + ctxt.lineTo(width, 0); + ctxt.closePath(); + var grad = ctxt.createLinearGradient(0, 0, 0, canvas.canvasSize.height); + grad.addColorStop(0, "rgba(255,128,128,.5)"); + grad.addColorStop(1, "rgba(255,0,0,.5)"); + ctxt.fillStyle = grad; + ctxt.fill(); +} + +function plot(canvas, ctxt, region) +{ + drawGraph(canvas, ctxt, region); + drawData(canvas, ctxt, region); +} + +function xScale(canvas) +{ + if (values.length === 0 && ranges.length === 0) + return; + + var width = canvas.canvasSize.width - xmargin; + + var sumValue = ranges[ranges.length - 1].start + ranges[ranges.length - 1].duration - ranges[0].start; + var spacing = sumValue / width; + return spacing; +} diff --git a/src/plugins/qmlprofiler/MainView.qml b/src/plugins/qmlprofiler/MainView.qml new file mode 100644 index 00000000000..c8fb2620550 --- /dev/null +++ b/src/plugins/qmlprofiler/MainView.qml @@ -0,0 +1,262 @@ +import QtQuick 1.1 +import Monitor 1.0 +import "MainView.js" as Plotter + +Rectangle { + id: root + + property variant colors: Plotter.colors //the colors used for the timeline data + property bool xRay: false //useful for seeing "nested" ranges (but redraw is buggy -- QGV problem?) + property Item currentItem //currently selected item in the view + + property bool zooming:false + + // move the cursor in the editor + signal updateCursorPosition + property string fileName: "" + property int lineNumber: -1 + function gotoSourceLocation(file,line) { + root.fileName = file; + root.lineNumber = line; + root.updateCursorPosition(); + } + + //handle debug data coming from C++ + Connections { + target: connection + onEvent: { + if (Plotter.valuesdone) { + Plotter.reset(); + view.clearData(); + rangeMover.x = 2 + rangeMover.opacity = 0 + } + + if (!Plotter.valuesdone && event === 0) //### only handle paint event + Plotter.values.push(time); + } + + onRange: { + if (Plotter.valuesdone) { + Plotter.reset(); + view.clearData(); + rangeMover.x = 2 + rangeMover.opacity = 0 + } + + if (!Plotter.valuesdone) + Plotter.ranges.push( { type: type, start: startTime, duration: length, label: data, fileName: fileName, line: line } ); + } + + onComplete: { + Plotter.valuesdone = true; + Plotter.calcFps(); + view.setRanges(Plotter.ranges); + view.updateTimeline(); + canvas.requestPaint(); + rangeMover.x = 1 //### hack to get view to display things immediately + rangeMover.opacity = 1 + } + + } + + //timeline background + Item { + anchors.fill: flick + Column { + anchors.fill: parent + + Repeater { + model: 5 //### values.length? + delegate: Rectangle { + width: parent.width + height: 50 //### + color: index % 2 ? "#fafafa" : "white" + } + } + } + } + + //our main interaction view + Flickable { + id: flick + anchors.top: parent.top + anchors.right: parent.right + anchors.left: labels.right + anchors.bottom: canvas.top + contentWidth: view.totalWidth + contentHeight: view.height + + TimelineView { + id: view + + width: flick.width; height: flick.height + + startX: flick.contentX + onStartXChanged: { + var newX = startTime / Plotter.xScale(canvas) - canvas.canvasWindow.x; + if (Math.abs(rangeMover.x - newX) > .01) + rangeMover.x = newX + if (Math.abs(startX - flick.contentX) > .01) + flick.contentX = startX + } + startTime: rangeMover.value + + property real prevXStep: -1 + property real possibleEndTime: startTime + (rangeMover.width*Plotter.xScale(canvas)) + onPossibleEndTimeChanged: { + var set = ((zooming && prevXStep != canvas.canvasWindow.x) || !zooming); + prevXStep = canvas.canvasWindow.x; + if (set) + endTime = possibleEndTime + } + onEndTimeChanged: updateTimeline() + + delegate: Rectangle { + id: obj + + property color baseColor: colors[type] + property color myColor: baseColor + + function conditionalHide() { + if (!mouseArea.containsMouse) + mouseArea.exited() + } + + height: 50 + gradient: Gradient { + GradientStop { position: 0.0; color: myColor } + GradientStop { position: 0.5; color: Qt.darker(myColor, 1.1) } + GradientStop { position: 1.0; color: myColor } + } + smooth: true + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onEntered: { + currentItem = obj + myColor = Qt.darker(baseColor, 1.2) + rangeDetails.duration = duration + rangeDetails.label = label + rangeDetails.file = fileName + rangeDetails.line = line + rangeDetails.type = Plotter.names[type] + + var pos = mapToItem(rangeDetails.parent, mouseX, y+height) + var preferredX = Math.max(10, pos.x - rangeDetails.width/2) + if (preferredX + rangeDetails.width > rangeDetails.parent.width) + preferredX = rangeDetails.parent.width - rangeDetails.width + rangeDetails.x = preferredX + + rangeDetails.y = pos.y + 10 + rangeDetails.visible = true + } + onExited: { + myColor = baseColor + rangeDetails.visible = false + rangeDetails.duration = "" + rangeDetails.label = "" + rangeDetails.type = "" + rangeDetails.file = "" + rangeDetails.line = -1 + } + onClicked: root.gotoSourceLocation(rangeDetails.file, rangeDetails.line); + } + } + } + } + + //popup showing the details for the hovered range + RangeDetails { + id: rangeDetails + } + + Rectangle { + id: labels + width: 150 + color: "#dcdcdc" + anchors.top: root.top + anchors.bottom: canvas.top + + Column { + //### change to use Repeater + Plotter.names? + Label { text: "Painting" } + Label { text: "Compiling" } + Label { text: "Creating" } + Label { text: "Binding" } + Label { text: "Signal Handler" } + } + + //right border divider + Rectangle { + width: 1 + height: parent.height + anchors.right: parent.right + color: "#cccccc" + } + } + + //bottom border divider + Rectangle { + height: 1 + width: parent.width + anchors.bottom: canvas.top + color: "#cccccc" + } + + //"overview" graph at the bottom + TiledCanvas { + id: canvas + + anchors.bottom: parent.bottom + width: parent.width; height: 50 + + property int canvasWidth: width + + canvasSize { + width: canvasWidth + height: canvas.height + } + + tileSize.width: width + tileSize.height: height + + canvasWindow.width: width + canvasWindow.height: 50 + + onDrawRegion: { + if (Plotter.valuesdone) + Plotter.plot(canvas, ctxt, region); + else + Plotter.drawGraph(canvas, ctxt, region) //just draw the background + } + } + + //moves the range mover to the position of a click + MouseArea { + anchors.fill: canvas + //### ideally we could press to position then immediately drag + onPressed: rangeMover.x = mouse.x - rangeMover.width/2 + } + + RangeMover { + id: rangeMover + opacity: 0 + anchors.top: canvas.top + } + + Rectangle { + width: 50 + height: 30 + anchors.right: root.right + anchors.top: root.top + radius: 4 + color: "#606085" + Elapsed { + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + } + } + +} diff --git a/src/plugins/qmlprofiler/QmlProfiler.pluginspec.in b/src/plugins/qmlprofiler/QmlProfiler.pluginspec.in new file mode 100644 index 00000000000..6bdcd694e72 --- /dev/null +++ b/src/plugins/qmlprofiler/QmlProfiler.pluginspec.in @@ -0,0 +1,20 @@ + + Nokia Corporation + (C) 2011 Nokia Corporation + + Commercial Usage + + Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Nokia. + + GNU Lesser General Public License Usage + + Alternatively, this plugin may be used under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. Please review the following information to ensure the GNU Lesser General Public License version 2.1 requirements will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + + Qml Profiler Plugin + http://qt.nokia.com + + + + + + diff --git a/src/plugins/qmlprofiler/RangeDetails.qml b/src/plugins/qmlprofiler/RangeDetails.qml new file mode 100644 index 00000000000..c5d5fd0ec35 --- /dev/null +++ b/src/plugins/qmlprofiler/RangeDetails.qml @@ -0,0 +1,61 @@ +import QtQuick 1.1 +import Monitor 1.0 +import "MainView.js" as Plotter + +BorderImage { + id: rangeDetails + + property string duration //###int? + property string label + property string type + property string file + property int line + + source: "popup.png" + border { + left: 10; top: 10 + right: 20; bottom: 20 + } + + width: col.width + 45 + height: childrenRect.height + 30 + z: 1 + visible: false + + //title + Text { + id: typeTitle + text: rangeDetails.type + font.bold: true + y: 10 + anchors.horizontalCenter: parent.horizontalCenter + anchors.horizontalCenterOffset: -5 + } + + //details + Column { + id: col + anchors.top: typeTitle.bottom + Detail { + label: "Duration" + content: rangeDetails.duration + "μs" + } + Detail { + opacity: content.length !== 0 ? 1 : 0 + label: "Details" + content: rangeDetails.label + } + Detail { + opacity: content.length !== 0 ? 1 : 0 + label: "Location" + content: { + var file = rangeDetails.file + var pos = file.lastIndexOf("/") + if (pos != -1) + file = file.substr(pos+1) + return (file.length !== 0) ? (file + ":" + rangeDetails.line) : ""; + } + onLinkActivated: Qt.openUrlExternally(url) + } + } +} diff --git a/src/plugins/qmlprofiler/RangeMover.qml b/src/plugins/qmlprofiler/RangeMover.qml new file mode 100644 index 00000000000..1247072bb55 --- /dev/null +++ b/src/plugins/qmlprofiler/RangeMover.qml @@ -0,0 +1,62 @@ +import "MainView.js" as Plotter +import QtQuick 1.1 +import Monitor 1.0 + +Item { + id: rangeMover + width: rect.width; height: 50 + + property real prevXStep: -1 + property real possibleValue: (canvas.canvasWindow.x + x) * Plotter.xScale(canvas) + onPossibleValueChanged: { + var set = (!zooming || (zooming && prevXStep != canvas.canvasWindow.x)) + prevXStep = canvas.canvasWindow.x; + if (set) + value = possibleValue + } + + property real value + //onValueChanged: console.log("*************** " + value) + + /*Image { + id: leftRange + source: "range.png" + anchors.horizontalCenter: parent.left + anchors.bottom: parent.bottom + }*/ + + Rectangle { + id: rect + + color: "#cc80b2f6" + width: 20 + //anchors.left: parent.left + //anchors.right: rightRange.horizontalCenter + + height: parent.height + } + + /*Image { + id: rightRange + source: "range.png" + x: 13 + anchors.bottom: parent.bottom + + MouseArea { + width: parent.width + height: 15 + drag.target: rightRange + drag.axis: "XAxis" + drag.minimumX: -7 //### + drag.maximumX: canvas.width - rangeMover.width //### + } + }*/ + + MouseArea { + anchors.fill: parent + drag.target: rangeMover + drag.axis: "XAxis" + drag.minimumX: 0 + drag.maximumX: canvas.width - rangeMover.width //### + } +} diff --git a/src/plugins/qmlprofiler/RecordButton.qml b/src/plugins/qmlprofiler/RecordButton.qml new file mode 100644 index 00000000000..29b764a22be --- /dev/null +++ b/src/plugins/qmlprofiler/RecordButton.qml @@ -0,0 +1,53 @@ +import QtQuick 1.1 + +Rectangle { + id: button + + property alias text: textItem.text + property bool recording: false + + signal clicked + + width: 80; height: textItem.height + 8 + border.width: 1 + border.color: Qt.darker(button.color, 1.4) + radius: height/2 + smooth: true + + color: "#049e0e" + + Text { + id: textItem + anchors.centerIn: parent + font.pixelSize: 14 + color: "white" + text: "Recording" + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: { + recording = !recording; + button.clicked() + } + } + + StateGroup { + id: recordState + states: State { + name: "recording" + when: recording + PropertyChanges { target: button; color: "#ff120c"; } + PropertyChanges { target: textItem; text: "Stop" } + } + } + + StateGroup { + states: State { + name: "pressed"; when: mouseArea.pressed && mouseArea.containsMouse + PropertyChanges { target: button; color: Qt.darker(color, 1.1); explicit: true } + PropertyChanges { target: textItem; x: textItem.x + 1; y: textItem.y + 1; explicit: true } + } + } +} diff --git a/src/plugins/qmlprofiler/ToolButton.qml b/src/plugins/qmlprofiler/ToolButton.qml new file mode 100644 index 00000000000..b2461684d8f --- /dev/null +++ b/src/plugins/qmlprofiler/ToolButton.qml @@ -0,0 +1,27 @@ +import QtQuick 1.1 +import Monitor 1.0 +import "MainView.js" as Plotter + +Rectangle { + property string label + signal clicked + + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: -1 + width: 30; height: 22 + + border.color: "#cc80b2f6" + color: "transparent" + + Text { + anchors.centerIn: parent + text: label + color: "white" + font.pixelSize: 14 + } + + MouseArea { + anchors.fill: parent + onClicked: parent.clicked() + } +} diff --git a/src/plugins/qmlprofiler/canvas/canvas.pri b/src/plugins/qmlprofiler/canvas/canvas.pri new file mode 100644 index 00000000000..2b37e8ee5d8 --- /dev/null +++ b/src/plugins/qmlprofiler/canvas/canvas.pri @@ -0,0 +1,11 @@ +INCLUDEPATH += $$PWD + +HEADERS += $$PWD/qdeclarativecontext2d_p.h \ + $$PWD/qdeclarativecanvas_p.h \ + $$PWD/qdeclarativetiledcanvas_p.h \ + $$PWD/qdeclarativecanvastimer_p.h + +SOURCES += $$PWD/qdeclarativecontext2d.cpp \ + $$PWD/qdeclarativecanvas.cpp \ + $$PWD/qdeclarativetiledcanvas.cpp \ + $$PWD/qdeclarativecanvastimer.cpp diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecanvas.cpp b/src/plugins/qmlprofiler/canvas/qdeclarativecanvas.cpp new file mode 100644 index 00000000000..0f7c7ae501a --- /dev/null +++ b/src/plugins/qmlprofiler/canvas/qdeclarativecanvas.cpp @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativecanvas_p.h" +#include "qdeclarativecanvastimer_p.h" +#include "qdeclarativecontext2d_p.h" + +#include + +QT_BEGIN_NAMESPACE + +Canvas::Canvas(QDeclarativeItem *parent) + : QDeclarativeItem(parent), + m_context(new Context2D(this)), + m_canvasWidth(0), + m_canvasHeight(0), + m_fillMode(Canvas::Stretch), + m_color(Qt::white) +{ + setFlag(QGraphicsItem::ItemHasNoContents, false); + setCacheMode(QGraphicsItem::DeviceCoordinateCache); +} + + +void Canvas::componentComplete() +{ + if (m_canvasWidth == 0 && m_canvasHeight == 0) + m_context->setSize(width(), height()); + else + m_context->setSize(m_canvasWidth, m_canvasHeight); + + connect(m_context, SIGNAL(changed()), this, SLOT(requestPaint())); + emit init(); + QDeclarativeItem::componentComplete(); +} + +void Canvas::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + m_context->setInPaint(true); + emit paint(); + + bool oldAA = painter->testRenderHint(QPainter::Antialiasing); + bool oldSmooth = painter->testRenderHint(QPainter::SmoothPixmapTransform); + if (smooth()) + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, smooth()); + + if (m_context->pixmap().isNull()) { + painter->fillRect(0, 0, width(), height(), m_color); + } else if (width() != m_context->pixmap().width() || height() != m_context->pixmap().height()) { + if (m_fillMode>= Tile) { + if (m_fillMode== Tile) { + painter->drawTiledPixmap(QRectF(0,0,width(),height()), m_context->pixmap()); + } else { + qreal widthScale = width() / qreal(m_context->pixmap().width()); + qreal heightScale = height() / qreal(m_context->pixmap().height()); + + QTransform scale; + if (m_fillMode== TileVertically) { + scale.scale(widthScale, 1.0); + QTransform old = painter->transform(); + painter->setWorldTransform(scale * old); + painter->drawTiledPixmap(QRectF(0,0,m_context->pixmap().width(),height()), m_context->pixmap()); + painter->setWorldTransform(old); + } else { + scale.scale(1.0, heightScale); + QTransform old = painter->transform(); + painter->setWorldTransform(scale * old); + painter->drawTiledPixmap(QRectF(0,0,width(),m_context->pixmap().height()), m_context->pixmap()); + painter->setWorldTransform(old); + } + } + } else { + qreal widthScale = width() / qreal(m_context->pixmap().width()); + qreal heightScale = height() / qreal(m_context->pixmap().height()); + + QTransform scale; + + if (m_fillMode== PreserveAspectFit) { + if (widthScale <= heightScale) { + heightScale = widthScale; + scale.translate(0, (height() - heightScale * m_context->pixmap().height()) / 2); + } else if (heightScale < widthScale) { + widthScale = heightScale; + scale.translate((width() - widthScale * m_context->pixmap().width()) / 2, 0); + } + } else if (m_fillMode== PreserveAspectCrop) { + if (widthScale < heightScale) { + widthScale = heightScale; + scale.translate((width() - widthScale * m_context->pixmap().width()) / 2, 0); + } else if (heightScale < widthScale) { + heightScale = widthScale; + scale.translate(0, (height() - heightScale * m_context->pixmap().height()) / 2); + } + } + if (clip()) { + painter->save(); + painter->setClipRect(boundingRect(), Qt::IntersectClip); + } + scale.scale(widthScale, heightScale); + QTransform old = painter->transform(); + painter->setWorldTransform(scale * old); + painter->drawPixmap(0, 0, m_context->pixmap()); + painter->setWorldTransform(old); + if (clip()) { + painter->restore(); + } + } + } else { + painter->drawPixmap(0, 0, m_context->pixmap()); + } + + if (smooth()) { + painter->setRenderHint(QPainter::Antialiasing, oldAA); + painter->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth); + } + m_context->setInPaint(false); +} + +Context2D *Canvas::getContext(const QString &contextId) +{ + if (contextId == QLatin1String("2d")) + return m_context; + qDebug("Canvas:requesting unsupported context"); + return 0; +} + +void Canvas::requestPaint() +{ + update(); +} + +void Canvas::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (m_canvasWidth == 0 && m_canvasHeight == 0 + && newGeometry.width() > 0 && newGeometry.height() > 0) { + m_context->setSize(width(), height()); + } + QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); +} + +void Canvas::setCanvasWidth(int newWidth) +{ + if (m_canvasWidth != newWidth) { + m_canvasWidth = newWidth; + m_context->setSize(m_canvasWidth, m_canvasHeight); + emit canvasWidthChanged(); + } +} + +void Canvas::setCanvasHeight(int newHeight) +{ + if (m_canvasHeight != newHeight) { + m_canvasHeight = newHeight; + m_context->setSize(m_canvasWidth, m_canvasHeight); + emit canvasHeightChanged(); + } +} + +void Canvas::setFillMode(FillMode mode) +{ + if (m_fillMode == mode) + return; + + m_fillMode = mode; + update(); + emit fillModeChanged(); +} + +QColor Canvas::color() +{ + return m_color; +} + +void Canvas::setColor(const QColor &color) +{ + if (m_color !=color) { + m_color = color; + colorChanged(); + } +} + +Canvas::FillMode Canvas::fillMode() const +{ + return m_fillMode; +} + +bool Canvas::save(const QString &filename) const +{ + return m_context->pixmap().save(filename); +} + +CanvasImage *Canvas::toImage() const +{ + return new CanvasImage(m_context->pixmap()); +} + +void Canvas::setTimeout(const QScriptValue &handler, long timeout) +{ + if (handler.isFunction()) + CanvasTimer::createTimer(this, handler, timeout, true); +} + +void Canvas::setInterval(const QScriptValue &handler, long interval) +{ + if (handler.isFunction()) + CanvasTimer::createTimer(this, handler, interval, false); +} + +void Canvas::clearTimeout(const QScriptValue &handler) +{ + CanvasTimer::removeTimer(handler); +} + +void Canvas::clearInterval(const QScriptValue &handler) +{ + CanvasTimer::removeTimer(handler); +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecanvas_p.h b/src/plugins/qmlprofiler/canvas/qdeclarativecanvas_p.h new file mode 100644 index 00000000000..72512258e54 --- /dev/null +++ b/src/plugins/qmlprofiler/canvas/qdeclarativecanvas_p.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECANVAS_P_H +#define QDECLARATIVECANVAS_P_H + +#include + +#include "qdeclarativecontext2d_p.h" +#include "qdeclarativecanvastimer_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class Canvas : public QDeclarativeItem +{ + Q_OBJECT + + Q_ENUMS(FillMode) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged); + Q_PROPERTY(int canvasWidth READ canvasWidth WRITE setCanvasWidth NOTIFY canvasWidthChanged); + Q_PROPERTY(int canvasHeight READ canvasHeight WRITE setCanvasHeight NOTIFY canvasHeightChanged); + Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged) + +public: + Canvas(QDeclarativeItem *parent = 0); + enum FillMode { Stretch, PreserveAspectFit, PreserveAspectCrop, Tile, TileVertically, TileHorizontally }; + + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + void setCanvasWidth(int newWidth); + int canvasWidth() {return m_canvasWidth;} + + void setCanvasHeight(int canvasHeight); + int canvasHeight() {return m_canvasHeight;} + + void componentComplete(); + + +public Q_SLOTS: + Context2D *getContext(const QString & = QString("2d")); + void requestPaint(); + + FillMode fillMode() const; + void setFillMode(FillMode); + + QColor color(); + void setColor(const QColor &); + + // Save current canvas to disk + bool save(const QString& filename) const; + + // Timers + void setInterval(const QScriptValue &handler, long timeout); + void setTimeout(const QScriptValue &handler, long timeout); + void clearInterval(const QScriptValue &handler); + void clearTimeout(const QScriptValue &handler); + +Q_SIGNALS: + void fillModeChanged(); + void canvasWidthChanged(); + void canvasHeightChanged(); + void colorChanged(); + void init(); + void paint(); + +private: + // Return canvas contents as a drawable image + CanvasImage *toImage() const; + Context2D *m_context; + int m_canvasWidth; + int m_canvasHeight; + FillMode m_fillMode; + QColor m_color; + + friend class Context2D; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif //QDECLARATIVECANVAS_P_H diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecanvastimer.cpp b/src/plugins/qmlprofiler/canvas/qdeclarativecanvastimer.cpp new file mode 100644 index 00000000000..64e6be08d58 --- /dev/null +++ b/src/plugins/qmlprofiler/canvas/qdeclarativecanvastimer.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include "qdeclarativecanvastimer_p.h" + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(QList , activeTimers); + +CanvasTimer::CanvasTimer(QObject *parent, const QScriptValue &data) + : QTimer(parent), m_value(data) +{ +} + +void CanvasTimer::handleTimeout() +{ + Q_ASSERT(m_value.isFunction()); + m_value.call(); + if (isSingleShot()) { + removeTimer(this); + } +} + +void CanvasTimer::createTimer(QObject *parent, const QScriptValue &val, long timeout, bool singleshot) +{ + + CanvasTimer *timer = new CanvasTimer(parent, val); + timer->setInterval(timeout); + timer->setSingleShot(singleshot); + connect(timer, SIGNAL(timeout()), timer, SLOT(handleTimeout())); + activeTimers()->append(timer); + timer->start(); +} + +void CanvasTimer::removeTimer(CanvasTimer *timer) +{ + activeTimers()->removeAll(timer); + timer->deleteLater(); +} + +void CanvasTimer::removeTimer(const QScriptValue &val) +{ + if (!val.isFunction()) + return; + + for (int i = 0 ; i < activeTimers()->count() ; ++i) { + CanvasTimer *timer = activeTimers()->at(i); + if (timer->equals(val)) { + removeTimer(timer); + return; + } + } +} + +QT_END_NAMESPACE + diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecanvastimer_p.h b/src/plugins/qmlprofiler/canvas/qdeclarativecanvastimer_p.h new file mode 100644 index 00000000000..3e55d0260ac --- /dev/null +++ b/src/plugins/qmlprofiler/canvas/qdeclarativecanvastimer_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECANVASTIMER_P_H +#define QDECLARATIVECANVASTIMER_P_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class CanvasTimer : public QTimer +{ + Q_OBJECT + +public: + CanvasTimer(QObject *parent, const QScriptValue &data); + +public Q_SLOTS: + void handleTimeout(); + bool equals(const QScriptValue &value){return m_value.equals(value);} + +public: + static void createTimer(QObject *parent, const QScriptValue &val, long timeout, bool singleshot); + static void removeTimer(CanvasTimer *timer); + static void removeTimer(const QScriptValue &); + +private: + QScriptValue m_value; + +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVECANVASTIMER_P_H diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecontext2d.cpp b/src/plugins/qmlprofiler/canvas/qdeclarativecontext2d.cpp new file mode 100644 index 00000000000..a98b707dbe8 --- /dev/null +++ b/src/plugins/qmlprofiler/canvas/qdeclarativecontext2d.cpp @@ -0,0 +1,1139 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativecontext2d_p.h" + +#include "qdeclarativecanvas_p.h" + +#include +#include + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +static const double Q_PI = 3.14159265358979323846; // pi + +class CustomDropShadowEffect : public QGraphicsDropShadowEffect +{ +public: + void draw(QPainter *painter) { QGraphicsDropShadowEffect::draw(painter);} + void drawSource(QPainter *painter) { QGraphicsDropShadowEffect::drawSource(painter);} +}; + +// Note, this is exported but in a private header as qtopengl depends on it. +// But it really should be considered private API +void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0); +void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0); + +#define DEGREES(t) ((t) * 180.0 / Q_PI) + +#define qClamp(val, min, max) qMin(qMax(val, min), max) +static QList parseNumbersList(QString::const_iterator &itr) +{ + QList points; + QString temp; + while ((*itr).isSpace()) + ++itr; + while ((*itr).isNumber() || + (*itr) == '-' || (*itr) == '+' || (*itr) == '.') { + temp = QString(); + + if ((*itr) == '-') + temp += *itr++; + else if ((*itr) == '+') + temp += *itr++; + while ((*itr).isDigit()) + temp += *itr++; + if ((*itr) == '.') + temp += *itr++; + while ((*itr).isDigit()) + temp += *itr++; + while ((*itr).isSpace()) + ++itr; + if ((*itr) == ',') + ++itr; + points.append(temp.toDouble()); + //eat spaces + while ((*itr).isSpace()) + ++itr; + } + + return points; +} + +QColor colorFromString(const QString &name) +{ + QString::const_iterator itr = name.constBegin(); + QList compo; + if (name.startsWith("rgba(")) { + ++itr; ++itr; ++itr; ++itr; ++itr; + compo = parseNumbersList(itr); + if (compo.size() != 4) { + return QColor(); + } + //alpha seems to be always between 0-1 + compo[3] *= 255; + return QColor((int)compo[0], (int)compo[1], + (int)compo[2], (int)compo[3]); + } else if (name.startsWith("rgb(")) { + ++itr; ++itr; ++itr; ++itr; + compo = parseNumbersList(itr); + if (compo.size() != 3) { + return QColor(); + } + return QColor((int)qClamp(compo[0], qreal(0), qreal(255)), + (int)qClamp(compo[1], qreal(0), qreal(255)), + (int)qClamp(compo[2], qreal(0), qreal(255))); + } else if (name.startsWith("hsla(")){ + ++itr; ++itr; ++itr; ++itr; ++itr; + compo = parseNumbersList(itr); + if (compo.size() != 4) { + return QColor(); + } + return QColor::fromHslF(compo[0], compo[1], + compo[2], compo[3]); + } else if (name.startsWith("hsl(")){ + ++itr; ++itr; ++itr; ++itr; ++itr; + compo = parseNumbersList(itr); + if (compo.size() != 3) { + return QColor(); + } + return QColor::fromHslF(compo[0], compo[1], + compo[2]); + } else { + //QRgb color; + //CSSParser::parseColor(name, color); + return QColor(name); + } +} + + +static QPainter::CompositionMode compositeOperatorFromString(const QString &compositeOperator) +{ + if (compositeOperator == QLatin1String("source-over")) { + return QPainter::CompositionMode_SourceOver; + } else if (compositeOperator == QLatin1String("source-out")) { + return QPainter::CompositionMode_SourceOut; + } else if (compositeOperator == QLatin1String("source-in")) { + return QPainter::CompositionMode_SourceIn; + } else if (compositeOperator == QLatin1String("source-atop")) { + return QPainter::CompositionMode_SourceAtop; + } else if (compositeOperator == QLatin1String("destination-atop")) { + return QPainter::CompositionMode_DestinationAtop; + } else if (compositeOperator == QLatin1String("destination-in")) { + return QPainter::CompositionMode_DestinationIn; + } else if (compositeOperator == QLatin1String("destination-out")) { + return QPainter::CompositionMode_DestinationOut; + } else if (compositeOperator == QLatin1String("destination-over")) { + return QPainter::CompositionMode_DestinationOver; + } else if (compositeOperator == QLatin1String("darker")) { + return QPainter::CompositionMode_SourceOver; + } else if (compositeOperator == QLatin1String("lighter")) { + return QPainter::CompositionMode_SourceOver; + } else if (compositeOperator == QLatin1String("copy")) { + return QPainter::CompositionMode_Source; + } else if (compositeOperator == QLatin1String("xor")) { + return QPainter::CompositionMode_Xor; + } + + return QPainter::CompositionMode_SourceOver; +} + +static QString compositeOperatorToString(QPainter::CompositionMode op) +{ + switch (op) { + case QPainter::CompositionMode_SourceOver: + return "source-over"; + case QPainter::CompositionMode_DestinationOver: + return "destination-over"; + case QPainter::CompositionMode_Clear: + return "clear"; + case QPainter::CompositionMode_Source: + return "source"; + case QPainter::CompositionMode_Destination: + return "destination"; + case QPainter::CompositionMode_SourceIn: + return "source-in"; + case QPainter::CompositionMode_DestinationIn: + return "destination-in"; + case QPainter::CompositionMode_SourceOut: + return "source-out"; + case QPainter::CompositionMode_DestinationOut: + return "destination-out"; + case QPainter::CompositionMode_SourceAtop: + return "source-atop"; + case QPainter::CompositionMode_DestinationAtop: + return "destination-atop"; + case QPainter::CompositionMode_Xor: + return "xor"; + case QPainter::CompositionMode_Plus: + return "plus"; + case QPainter::CompositionMode_Multiply: + return "multiply"; + case QPainter::CompositionMode_Screen: + return "screen"; + case QPainter::CompositionMode_Overlay: + return "overlay"; + case QPainter::CompositionMode_Darken: + return "darken"; + case QPainter::CompositionMode_Lighten: + return "lighten"; + case QPainter::CompositionMode_ColorDodge: + return "color-dodge"; + case QPainter::CompositionMode_ColorBurn: + return "color-burn"; + case QPainter::CompositionMode_HardLight: + return "hard-light"; + case QPainter::CompositionMode_SoftLight: + return "soft-light"; + case QPainter::CompositionMode_Difference: + return "difference"; + case QPainter::CompositionMode_Exclusion: + return "exclusion"; + default: + break; + } + return QString(); +} + +void Context2D::save() +{ + m_stateStack.push(m_state); +} + + +void Context2D::restore() +{ + if (!m_stateStack.isEmpty()) { + m_state = m_stateStack.pop(); + m_state.flags = AllIsFullOfDirt; + } +} + + +void Context2D::scale(qreal x, qreal y) +{ + m_state.matrix.scale(x, y); + m_state.flags |= DirtyTransformationMatrix; +} + + +void Context2D::rotate(qreal angle) +{ + m_state.matrix.rotate(DEGREES(angle)); + m_state.flags |= DirtyTransformationMatrix; +} + + +void Context2D::translate(qreal x, qreal y) +{ + m_state.matrix.translate(x, y); + m_state.flags |= DirtyTransformationMatrix; +} + + +void Context2D::transform(qreal m11, qreal m12, qreal m21, qreal m22, + qreal dx, qreal dy) +{ + QMatrix mat(m11, m12, + m21, m22, + dx, dy); + m_state.matrix *= mat; + m_state.flags |= DirtyTransformationMatrix; +} + + +void Context2D::setTransform(qreal m11, qreal m12, qreal m21, qreal m22, + qreal dx, qreal dy) +{ + QMatrix mat(m11, m12, + m21, m22, + dx, dy); + m_state.matrix = mat; + m_state.flags |= DirtyTransformationMatrix; +} + + +QString Context2D::globalCompositeOperation() const +{ + return compositeOperatorToString(m_state.globalCompositeOperation); +} + +void Context2D::setGlobalCompositeOperation(const QString &op) +{ + QPainter::CompositionMode mode = + compositeOperatorFromString(op); + m_state.globalCompositeOperation = mode; + m_state.flags |= DirtyGlobalCompositeOperation; +} + +QVariant Context2D::strokeStyle() const +{ + return m_state.strokeStyle; +} + +void Context2D::setStrokeStyle(const QVariant &style) +{ + CanvasGradient * gradient= qobject_cast(style.value()); + if (gradient) { + m_state.strokeStyle = gradient->value(); + } else { + QColor color = colorFromString(style.toString()); + m_state.strokeStyle = color; + } + m_state.flags |= DirtyStrokeStyle; +} + +QVariant Context2D::fillStyle() const +{ + return m_state.fillStyle; +} + +void Context2D::setFillStyle(const QVariant &style) +{ + CanvasGradient * gradient= qobject_cast(style.value()); + if (gradient) { + m_state.fillStyle = gradient->value(); + } else { + QColor color = colorFromString(style.toString()); + m_state.fillStyle = color; + } + m_state.flags |= DirtyFillStyle; +} + +qreal Context2D::globalAlpha() const +{ + return m_state.globalAlpha; +} + +void Context2D::setGlobalAlpha(qreal alpha) +{ + m_state.globalAlpha = alpha; + m_state.flags |= DirtyGlobalAlpha; +} + +CanvasImage *Context2D::createImage(const QString &url) +{ + return new CanvasImage(url); +} + +CanvasGradient *Context2D::createLinearGradient(qreal x0, qreal y0, + qreal x1, qreal y1) +{ + QLinearGradient g(x0, y0, x1, y1); + return new CanvasGradient(g); +} + + +CanvasGradient *Context2D::createRadialGradient(qreal x0, qreal y0, + qreal r0, qreal x1, + qreal y1, qreal r1) +{ + QRadialGradient g(QPointF(x1, y1), r0+r1, QPointF(x0, y0)); + return new CanvasGradient(g); +} + +qreal Context2D::lineWidth() const +{ + return m_state.lineWidth; +} + +void Context2D::setLineWidth(qreal w) +{ + m_state.lineWidth = w; + m_state.flags |= DirtyLineWidth; +} + +QString Context2D::lineCap() const +{ + switch (m_state.lineCap) { + case Qt::FlatCap: + return "butt"; + case Qt::SquareCap: + return "square"; + case Qt::RoundCap: + return "round"; + default: ; + } + return QString(); +} + +void Context2D::setLineCap(const QString &capString) +{ + Qt::PenCapStyle style; + if (capString == QLatin1String("round")) + style = Qt::RoundCap; + else if (capString == QLatin1String("square")) + style = Qt::SquareCap; + else //if (capString == "butt") + style = Qt::FlatCap; + m_state.lineCap = style; + m_state.flags |= DirtyLineCap; +} + +QString Context2D::lineJoin() const +{ + switch (m_state.lineJoin) { + case Qt::RoundJoin: + return QLatin1String("round"); + case Qt::BevelJoin: + return QLatin1String("bevel"); + case Qt::MiterJoin: + return QLatin1String("miter"); + default: ; + } + return QString(); +} + +void Context2D::setLineJoin(const QString &joinString) +{ + Qt::PenJoinStyle style; + if (joinString == QLatin1String("round")) + style = Qt::RoundJoin; + else if (joinString == QLatin1String("bevel")) + style = Qt::BevelJoin; + else //if (joinString == "miter") + style = Qt::MiterJoin; + m_state.lineJoin = style; + m_state.flags |= DirtyLineJoin; +} + +qreal Context2D::miterLimit() const +{ + return m_state.miterLimit; +} + +void Context2D::setMiterLimit(qreal m) +{ + m_state.miterLimit = m; + m_state.flags |= DirtyMiterLimit; +} + +void Context2D::setShadowOffsetX(qreal x) +{ + if (m_state.shadowOffsetX == x) + return; + m_state.shadowOffsetX = x; + updateShadowBuffer(); + if (m_painter.device() == &m_shadowbuffer && m_state.shadowBlur>0) + endPainting(); + m_state.flags |= DirtyShadowOffsetX; +} + +const QList &Context2D::mouseAreas() const +{ + return m_mouseAreas; +} + +void Context2D::updateShadowBuffer() { + if (m_shadowbuffer.isNull() || m_shadowbuffer.width() != m_width+m_state.shadowOffsetX || + m_shadowbuffer.height() != m_height+m_state.shadowOffsetY) { + m_shadowbuffer = QImage(m_width+m_state.shadowOffsetX, m_height+m_state.shadowOffsetY, QImage::Format_ARGB32); + m_shadowbuffer.fill(Qt::transparent); + } +} + +void Context2D::setShadowOffsetY(qreal y) +{ + if (m_state.shadowOffsetY == y) + return; + m_state.shadowOffsetY = y; + updateShadowBuffer(); + if (m_painter.device() == &m_shadowbuffer && m_state.shadowBlur>0) + endPainting(); + + m_state.flags |= DirtyShadowOffsetY; +} + +void Context2D::setShadowBlur(qreal b) +{ + if (m_state.shadowBlur == b) + return; + m_state.shadowBlur = b; + updateShadowBuffer(); + if (m_painter.device() == &m_shadowbuffer && m_state.shadowBlur>0) + endPainting(); + m_state.flags |= DirtyShadowBlur; +} + +void Context2D::setShadowColor(const QString &str) +{ + m_state.shadowColor = colorFromString(str); + if (m_painter.device() == &m_shadowbuffer && m_state.shadowBlur>0) + endPainting(); + m_state.flags |= DirtyShadowColor; +} + +QString Context2D::textBaseline() +{ + switch (m_state.textBaseline) { + case Context2D::Alphabetic: + return QLatin1String("alphabetic"); + case Context2D::Hanging: + return QLatin1String("hanging"); + case Context2D::Bottom: + return QLatin1String("bottom"); + case Context2D::Top: + return QLatin1String("top"); + case Context2D::Middle: + return QLatin1String("middle"); + default: + Q_ASSERT("invalid value"); + return QLatin1String("start"); + } +} + +void Context2D::setTextBaseline(const QString &baseline) +{ + if (baseline==QLatin1String("alphabetic")) + m_state.textBaseline = Context2D::Alphabetic; + else if (baseline == QLatin1String("hanging")) + m_state.textBaseline = Context2D::Hanging; + else if (baseline == QLatin1String("top")) + m_state.textBaseline = Context2D::Top; + else if (baseline == QLatin1String("bottom")) + m_state.textBaseline = Context2D::Bottom; + else if (baseline == QLatin1String("middle")) + m_state.textBaseline = Context2D::Middle; + else { + m_state.textBaseline = Context2D::Alphabetic; + qWarning() << ("Context2D: invalid baseline:" + baseline); + } + m_state.flags |= DirtyTextBaseline; +} + +QString Context2D::textAlign() +{ + switch (m_state.textAlign) { + case Context2D::Left: + return QLatin1String("left"); + case Context2D::Right: + return QLatin1String("right"); + case Context2D::Center: + return QLatin1String("center"); + case Context2D::Start: + return QLatin1String("start"); + case Context2D::End: + return QLatin1String("end"); + default: + Q_ASSERT("invalid value"); + qWarning() << ("Context2D::invalid textAlign"); + return QLatin1String("start"); + } +} + +void Context2D::setTextAlign(const QString &baseline) +{ + if (baseline==QLatin1String("start")) + m_state.textAlign = Context2D::Start; + else if (baseline == QLatin1String("end")) + m_state.textAlign = Context2D::End; + else if (baseline == QLatin1String("left")) + m_state.textAlign = Context2D::Left; + else if (baseline == QLatin1String("right")) + m_state.textAlign = Context2D::Right; + else if (baseline == QLatin1String("center")) + m_state.textAlign = Context2D::Center; + else { + m_state.textAlign= Context2D::Start; + qWarning("Context2D: invalid text align"); + } + // ### alphabetic, ideographic, hanging + m_state.flags |= DirtyTextBaseline; +} + +void Context2D::setFont(const QString &fontString) +{ + QFont font; + // ### this is simplified and incomplete + QStringList tokens = fontString.split(" "); + foreach (const QString &token, tokens) { + if (token == QLatin1String("italic")) + font.setItalic(true); + else if (token == QLatin1String("bold")) + font.setBold(true); + else if (token.endsWith(QLatin1String("px"))) { + QString number = token; + number.remove("px"); + font.setPointSizeF(number.trimmed().toFloat()); + } else + font.setFamily(token); + } + m_state.font = font; + m_state.flags |= DirtyFont; +} + +QString Context2D::font() +{ + return m_state.font.toString(); +} + +qreal Context2D::shadowOffsetX() const +{ + return m_state.shadowOffsetX; +} + +qreal Context2D::shadowOffsetY() const +{ + return m_state.shadowOffsetY; +} + + +qreal Context2D::shadowBlur() const +{ + return m_state.shadowBlur; +} + + +QString Context2D::shadowColor() const +{ + return m_state.shadowColor.name(); +} + + +void Context2D::clearRect(qreal x, qreal y, qreal w, qreal h) +{ + beginPainting(); + m_painter.save(); + m_painter.setMatrix(worldMatrix(), false); + m_painter.setCompositionMode(QPainter::CompositionMode_Source); + QColor fillColor = parent()->property("color").value(); + + m_painter.fillRect(QRectF(x, y, w, h), fillColor); + m_painter.restore(); + scheduleChange(); +} + +void Context2D::fillRect(qreal x, qreal y, qreal w, qreal h) +{ + beginPainting(); + m_painter.save(); + m_painter.setMatrix(worldMatrix(), false); + m_painter.fillRect(QRectF(x, y, w, h), m_painter.brush()); + m_painter.restore(); + scheduleChange(); +} + +int Context2D::baseLineOffset(Context2D::TextBaseLine value, const QFontMetrics &metrics) +{ + int offset = 0; + switch (value) { + case Context2D::Top: + break; + case Context2D::Alphabetic: + case Context2D::Middle: + case Context2D::Hanging: + offset = metrics.ascent(); + break; + case Context2D::Bottom: + offset = metrics.height(); + break; + } + return offset; +} + +int Context2D::textAlignOffset(Context2D::TextAlign value, const QFontMetrics &metrics, const QString &text) +{ + int offset = 0; + if (value == Context2D::Start) + value = qApp->layoutDirection() == Qt::LeftToRight ? Context2D::Left : Context2D::Right; + else if (value == Context2D::End) + value = qApp->layoutDirection() == Qt::LeftToRight ? Context2D::Right: Context2D::Left; + switch (value) { + case Context2D::Center: + offset = metrics.width(text)/2; + break; + case Context2D::Right: + offset = metrics.width(text); + case Context2D::Left: + default: + break; + } + return offset; +} + +void Context2D::fillText(const QString &text, qreal x, qreal y) +{ + beginPainting(); + m_painter.save(); + m_painter.setPen(QPen(m_state.fillStyle, m_state.lineWidth)); + m_painter.setMatrix(worldMatrix(), false); + QFont font; + font.setBold(true); + m_painter.setFont(m_state.font); + int yoffset = baseLineOffset(m_state.textBaseline, m_painter.fontMetrics()); + int xoffset = textAlignOffset(m_state.textAlign, m_painter.fontMetrics(), text); + QTextOption opt; // Adjust baseLine etc + m_painter.drawText(QRectF(x-xoffset, y-yoffset, QWIDGETSIZE_MAX, m_painter.fontMetrics().height()), text, opt); + m_painter.restore(); + endPainting(); + scheduleChange(); +} + +void Context2D::strokeText(const QString &text, qreal x, qreal y) +{ + beginPainting(); + m_painter.save(); + m_painter.setPen(QPen(m_state.fillStyle,0)); + m_painter.setMatrix(worldMatrix(), false); + + QPainterPath textPath; + QFont font = m_state.font; + font.setStyleStrategy(QFont::ForceOutline); + m_painter.setFont(font); + const QFontMetrics &metrics = m_painter.fontMetrics(); + int yoffset = baseLineOffset(m_state.textBaseline, metrics); + int xoffset = textAlignOffset(m_state.textAlign, metrics, text); + textPath.addText(x-xoffset, y-yoffset+metrics.ascent(), font, text); + m_painter.strokePath(textPath, QPen(m_state.fillStyle, m_state.lineWidth)); + m_painter.restore(); + endPainting(); + scheduleChange(); +} + +void Context2D::strokeRect(qreal x, qreal y, qreal w, qreal h) +{ + QPainterPath path; + path.addRect(x, y, w, h); + beginPainting(); + m_painter.save(); + m_painter.setMatrix(worldMatrix(), false); + m_painter.strokePath(path, m_painter.pen()); + m_painter.restore(); + scheduleChange(); +} + +void Context2D::mouseArea(qreal x, qreal y, qreal w, qreal h, const QScriptValue &callback, + const QScriptValue &data) +{ + MouseArea a = { callback, data, QRectF(x, y, w, h), m_state.matrix }; + m_mouseAreas << a; +} + +void Context2D::beginPath() +{ + m_path = QPainterPath(); +} + + +void Context2D::closePath() +{ + m_path.closeSubpath(); +} + + +void Context2D::moveTo(qreal x, qreal y) +{ + QPointF pt = worldMatrix().map(QPointF(x, y)); + m_path.moveTo(pt); +} + + +void Context2D::lineTo(qreal x, qreal y) +{ + QPointF pt = worldMatrix().map(QPointF(x, y)); + m_path.lineTo(pt); +} + + +void Context2D::quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y) +{ + QPointF cp = worldMatrix().map(QPointF(cpx, cpy)); + QPointF xy = worldMatrix().map(QPointF(x, y)); + m_path.quadTo(cp, xy); +} + + +void Context2D::bezierCurveTo(qreal cp1x, qreal cp1y, + qreal cp2x, qreal cp2y, qreal x, qreal y) +{ + QPointF cp1 = worldMatrix().map(QPointF(cp1x, cp1y)); + QPointF cp2 = worldMatrix().map(QPointF(cp2x, cp2y)); + QPointF end = worldMatrix().map(QPointF(x, y)); + m_path.cubicTo(cp1, cp2, end); +} + + +void Context2D::arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius) +{ + //FIXME: this is surely busted + QPointF st = worldMatrix().map(QPointF(x1, y1)); + QPointF end = worldMatrix().map(QPointF(x2, y2)); + m_path.arcTo(st.x(), st.y(), + end.x()-st.x(), end.y()-st.y(), + radius, 90); +} + + +void Context2D::rect(qreal x, qreal y, qreal w, qreal h) +{ + QPainterPath path; path.addRect(x, y, w, h); + path = worldMatrix().map(path); + m_path.addPath(path); +} + +void Context2D::arc(qreal xc, qreal yc, qreal radius, + qreal sar, qreal ear, + bool anticlockwise) +{ + //### HACK + // In Qt we don't switch the coordinate system for degrees + // and still use the 0,0 as bottom left for degrees so we need + // to switch + sar = -sar; + ear = -ear; + anticlockwise = !anticlockwise; + //end hack + + float sa = DEGREES(sar); + float ea = DEGREES(ear); + + double span = 0; + + double xs = xc - radius; + double ys = yc - radius; + double width = radius*2; + double height = radius*2; + + if (!anticlockwise && (ea < sa)) { + span += 360; + } else if (anticlockwise && (sa < ea)) { + span -= 360; + } + + //### this is also due to switched coordinate system + // we would end up with a 0 span instead of 360 + if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) && + qFuzzyCompare(qAbs(span), 360))) { + span += ea - sa; + } + + QPainterPath path; + path.moveTo(QPointF(xc + radius * cos(sar), + yc - radius * sin(sar))); + + path.arcTo(xs, ys, width, height, sa, span); + path = worldMatrix().map(path); + m_path.addPath(path); +} + + +void Context2D::fill() +{ + beginPainting(); + m_painter.fillPath(m_path, m_painter.brush()); + scheduleChange(); +} + + +void Context2D::stroke() +{ + beginPainting(); + m_painter.save(); + m_painter.setMatrix(worldMatrix(), false); + QPainterPath tmp = worldMatrix().inverted().map(m_path); + m_painter.strokePath(tmp, m_painter.pen()); + m_painter.restore(); + scheduleChange(); +} + + +void Context2D::clip() +{ + m_state.clipPath = m_path; + m_state.flags |= DirtyClippingRegion; +} + + +bool Context2D::isPointInPath(qreal x, qreal y) const +{ + return m_path.contains(QPointF(x, y)); +} + + +ImageData Context2D::getImageData(qreal sx, qreal sy, qreal sw, qreal sh) +{ + Q_UNUSED(sx); + Q_UNUSED(sy); + Q_UNUSED(sw); + Q_UNUSED(sh); + return ImageData(); +} + + +void Context2D::putImageData(ImageData image, qreal dx, qreal dy) +{ + Q_UNUSED(image); + Q_UNUSED(dx); + Q_UNUSED(dy); +} + +Context2D::Context2D(QObject *parent) + : QObject(parent), m_changeTimerId(-1), m_width(0), m_height(0), m_inPaint(false) +{ + reset(); +} + +void Context2D::setupPainter() +{ + m_painter.setRenderHint(QPainter::Antialiasing, true); + if ((m_state.flags & DirtyClippingRegion) && !m_state.clipPath.isEmpty()) + m_painter.setClipPath(m_state.clipPath); + if (m_state.flags & DirtyFillStyle) + m_painter.setBrush(m_state.fillStyle); + if (m_state.flags & DirtyGlobalAlpha) + m_painter.setOpacity(m_state.globalAlpha); + if (m_state.flags & DirtyGlobalCompositeOperation) + m_painter.setCompositionMode(m_state.globalCompositeOperation); + if (m_state.flags & MDirtyPen) { + QPen pen = m_painter.pen(); + if (m_state.flags & DirtyStrokeStyle) + pen.setBrush(m_state.strokeStyle); + if (m_state.flags & DirtyLineWidth) + pen.setWidthF(m_state.lineWidth); + if (m_state.flags & DirtyLineCap) + pen.setCapStyle(m_state.lineCap); + if (m_state.flags & DirtyLineJoin) + pen.setJoinStyle(m_state.lineJoin); + if (m_state.flags & DirtyMiterLimit) + pen.setMiterLimit(m_state.miterLimit); + m_painter.setPen(pen); + } +} + +void Context2D::beginPainting() +{ + if (m_width <= 0 || m_height <=0) + return; + + if (m_pixmap.width() != m_width || m_pixmap.height() != m_height) { + m_pixmap = QPixmap(m_width, m_height); + m_pixmap.fill(parent()->property("color").value()); + } + + if (m_state.shadowBlur > 0 && m_painter.device() != &m_shadowbuffer) { + if (m_painter.isActive()) + m_painter.end(); + updateShadowBuffer(); + m_painter.begin(&m_shadowbuffer); + m_painter.setViewport(m_state.shadowOffsetX, + m_state.shadowOffsetY, + m_shadowbuffer.width(), + m_shadowbuffer.height()); + m_shadowbuffer.fill(Qt::transparent); + } + + if (!m_painter.isActive()) { + m_painter.begin(&m_pixmap); + m_painter.setRenderHint(QPainter::Antialiasing); + if (!m_state.clipPath.isEmpty()) + m_painter.setClipPath(m_state.clipPath); + m_painter.setBrush(m_state.fillStyle); + m_painter.setOpacity(m_state.globalAlpha); + QPen pen; + pen.setBrush(m_state.strokeStyle); + if (pen.style() == Qt::NoPen) + pen.setStyle(Qt::SolidLine); + pen.setCapStyle(m_state.lineCap); + pen.setJoinStyle(m_state.lineJoin); + pen.setWidthF(m_state.lineWidth); + pen.setMiterLimit(m_state.miterLimit); + m_painter.setPen(pen); + } else { + setupPainter(); + m_state.flags = 0; + } +} + +void Context2D::endPainting() +{ + if (m_state.shadowBlur > 0) { + QImage alphaChannel = m_shadowbuffer.alphaChannel(); + + qt_blurImage(alphaChannel, m_state.shadowBlur, false, 1); + + QRect imageRect = m_shadowbuffer.rect(); + + if (m_shadowColorIndexBuffer.isEmpty() || m_shadowColorBuffer != m_state.shadowColor) { + m_shadowColorIndexBuffer.clear(); + m_shadowColorBuffer = m_state.shadowColor; + + for (int i = 0; i < 256; ++i) { + m_shadowColorIndexBuffer << qRgba(qRound(255 * m_state.shadowColor.redF()), + qRound(255 * m_state.shadowColor.greenF()), + qRound(255 * m_state.shadowColor.blueF()), + i); + } + } + alphaChannel.setColorTable(m_shadowColorIndexBuffer); + + if (m_painter.isActive()) + m_painter.end(); + + m_painter.begin(&m_pixmap); + + // draw the blurred drop shadow... + m_painter.save(); + QTransform tf = m_painter.transform(); + m_painter.translate(0, imageRect.height()); + m_painter.rotate(-90); + m_painter.drawImage(0, 0, alphaChannel); + m_painter.setTransform(tf); + m_painter.restore(); + + // draw source + m_painter.drawImage(-m_state.shadowOffsetX, -m_state.shadowOffsetY, m_shadowbuffer.copy()); + m_painter.end(); + } +} + +void Context2D::clear() +{ + m_painter.fillRect(QRect(QPoint(0,0), size()), Qt::white); +} + +void Context2D::reset() +{ + m_stateStack.clear(); + m_state.matrix = QMatrix(); + m_state.clipPath = QPainterPath(); + m_state.strokeStyle = Qt::black; + m_state.fillStyle = Qt::black; + m_state.globalAlpha = 1.0; + m_state.lineWidth = 1; + m_state.lineCap = Qt::FlatCap; + m_state.lineJoin = Qt::MiterJoin; + m_state.miterLimit = 10; + m_state.shadowOffsetX = 0; + m_state.shadowOffsetY = 0; + m_state.shadowBlur = 0; + m_state.shadowColor = qRgba(0, 0, 0, 0); + m_state.globalCompositeOperation = QPainter::CompositionMode_SourceOver; + m_state.font = QFont(); + m_state.textAlign = Start; + m_state.textBaseline = Alphabetic; + m_state.flags = AllIsFullOfDirt; + m_mouseAreas.clear(); + clear(); +} + +void Context2D::drawImage(const QVariant &var, qreal sx, qreal sy, + qreal sw = 0, qreal sh = 0) +{ + CanvasImage *image = qobject_cast(var.value()); + if (!image) { + Canvas *canvas = qobject_cast(var.value()); + if (canvas) + image = canvas->toImage(); + } + if (image) { + beginPainting(); + if (sw == sh && sh == 0) + m_painter.drawPixmap(QPointF(sx, sy), image->value()); + else + m_painter.drawPixmap(QRect(sx, sy, sw, sh), image->value()); + + scheduleChange(); + } +} + +void Context2D::setSize(int width, int height) +{ + endPainting(); + m_width = width; + m_height = height; + + scheduleChange(); +} + +void Context2D::setSize(const QSize &size) +{ + setSize(size.width(), size.height()); +} + +QSize Context2D::size() const +{ + return m_pixmap.size(); +} + +QPoint Context2D::painterTranslate() const +{ + return m_painterTranslate; +} + +void Context2D::setPainterTranslate(const QPoint &translate) +{ + m_painterTranslate = translate; + m_state.flags |= DirtyTransformationMatrix; +} + +void Context2D::scheduleChange() +{ + if (m_changeTimerId == -1 && !m_inPaint) + m_changeTimerId = startTimer(0); +} + +void Context2D::timerEvent(QTimerEvent *e) +{ + if (e->timerId() == m_changeTimerId) { + killTimer(m_changeTimerId); + m_changeTimerId = -1; + endPainting(); + emit changed(); + } else { + QObject::timerEvent(e); + } +} + +QMatrix Context2D::worldMatrix() const +{ + QMatrix mat; + mat.translate(m_painterTranslate.x(), m_painterTranslate.y()); + mat *= m_state.matrix; + return mat; +} + +QT_END_NAMESPACE diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativecontext2d_p.h b/src/plugins/qmlprofiler/canvas/qdeclarativecontext2d_p.h new file mode 100644 index 00000000000..11555c21abe --- /dev/null +++ b/src/plugins/qmlprofiler/canvas/qdeclarativecontext2d_p.h @@ -0,0 +1,342 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECONTEXT2D_P_H +#define QDECLARATIVECONTEXT2D_P_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +QColor colorFromString(const QString &name); + +class CanvasGradient : public QObject +{ + Q_OBJECT +public: + CanvasGradient(const QGradient &gradient) : m_gradient(gradient) {} + +public slots: + QGradient value() { return m_gradient; } + void addColorStop(float pos, const QString &color) { m_gradient.setColorAt(pos, colorFromString(color));} + +public: + QGradient m_gradient; +}; + +Q_DECLARE_METATYPE(CanvasGradient*) + + +class CanvasImage: public QObject +{ + Q_OBJECT + Q_PROPERTY(QString src READ src WRITE setSrc NOTIFY sourceChanged) + Q_PROPERTY(int width READ width) + Q_PROPERTY(int height READ height) + +public: + CanvasImage() {} + CanvasImage(const QString &url) : m_image(url), m_src(url) {} + CanvasImage(const QPixmap &pixmap) {m_image = pixmap;} + +public slots: + int width() { return m_image.width(); } + int height() { return m_image.height(); } + QPixmap &value() { return m_image; } + QString src() { return m_src; } + void setSrc(const QString &src) { m_src = src; m_image.load(src); emit sourceChanged();} +signals: + void sourceChanged(); + +private: + QPixmap m_image; + QString m_src; +}; + +Q_DECLARE_METATYPE(CanvasImage*) + + +class ImageData { +}; + +class Context2D : public QObject +{ + Q_OBJECT + // compositing + Q_PROPERTY(qreal globalAlpha READ globalAlpha WRITE setGlobalAlpha) + Q_PROPERTY(QString globalCompositeOperation READ globalCompositeOperation WRITE setGlobalCompositeOperation) + Q_PROPERTY(QVariant strokeStyle READ strokeStyle WRITE setStrokeStyle) + Q_PROPERTY(QVariant fillStyle READ fillStyle WRITE setFillStyle) + // line caps/joins + Q_PROPERTY(qreal lineWidth READ lineWidth WRITE setLineWidth) + Q_PROPERTY(QString lineCap READ lineCap WRITE setLineCap) + Q_PROPERTY(QString lineJoin READ lineJoin WRITE setLineJoin) + Q_PROPERTY(qreal miterLimit READ miterLimit WRITE setMiterLimit) + // shadows + Q_PROPERTY(qreal shadowOffsetX READ shadowOffsetX WRITE setShadowOffsetX) + Q_PROPERTY(qreal shadowOffsetY READ shadowOffsetY WRITE setShadowOffsetY) + Q_PROPERTY(qreal shadowBlur READ shadowBlur WRITE setShadowBlur) + Q_PROPERTY(QString shadowColor READ shadowColor WRITE setShadowColor) + // fonts + Q_PROPERTY(QString font READ font WRITE setFont) + Q_PROPERTY(QString textBaseline READ textBaseline WRITE setTextBaseline) + Q_PROPERTY(QString textAlign READ textAlign WRITE setTextAlign) + + enum TextBaseLine { Alphabetic=0, Top, Middle, Bottom, Hanging}; + enum TextAlign { Start=0, End, Left, Right, Center}; + +public: + Context2D(QObject *parent = 0); + void setSize(int width, int height); + void setSize(const QSize &size); + QSize size() const; + + QPoint painterTranslate() const; + void setPainterTranslate(const QPoint &); + + void scheduleChange(); + void timerEvent(QTimerEvent *e); + + void clear(); + void reset(); + + QPixmap pixmap() { return m_pixmap; } + + // compositing + qreal globalAlpha() const; // (default 1.0) + QString globalCompositeOperation() const; // (default over) + QVariant strokeStyle() const; // (default black) + QVariant fillStyle() const; // (default black) + + void setGlobalAlpha(qreal alpha); + void setGlobalCompositeOperation(const QString &op); + void setStrokeStyle(const QVariant &style); + void setFillStyle(const QVariant &style); + + // line caps/joins + qreal lineWidth() const; // (default 1) + QString lineCap() const; // "butt", "round", "square" (default "butt") + QString lineJoin() const; // "round", "bevel", "miter" (default "miter") + qreal miterLimit() const; // (default 10) + + void setLineWidth(qreal w); + void setLineCap(const QString &s); + void setLineJoin(const QString &s); + void setMiterLimit(qreal m); + + void setFont(const QString &font); + QString font(); + void setTextBaseline(const QString &font); + QString textBaseline(); + void setTextAlign(const QString &font); + QString textAlign(); + + // shadows + qreal shadowOffsetX() const; // (default 0) + qreal shadowOffsetY() const; // (default 0) + qreal shadowBlur() const; // (default 0) + QString shadowColor() const; // (default black) + + void setShadowOffsetX(qreal x); + void setShadowOffsetY(qreal y); + void setShadowBlur(qreal b); + void setShadowColor(const QString &str); + + struct MouseArea { + QScriptValue callback; + QScriptValue data; + QRectF rect; + QMatrix matrix; + }; + const QList &mouseAreas() const; + +public slots: + void save(); // push state on state stack + void restore(); // pop state stack and restore state + + void fillText(const QString &text, qreal x, qreal y); + void strokeText(const QString &text, qreal x, qreal y); + + void setInPaint(bool val){m_inPaint = val;} + void scale(qreal x, qreal y); + void rotate(qreal angle); + void translate(qreal x, qreal y); + void transform(qreal m11, qreal m12, qreal m21, qreal m22, + qreal dx, qreal dy); + void setTransform(qreal m11, qreal m12, qreal m21, qreal m22, + qreal dx, qreal dy); + + CanvasGradient *createLinearGradient(qreal x0, qreal y0, + qreal x1, qreal y1); + CanvasGradient *createRadialGradient(qreal x0, qreal y0, + qreal r0, qreal x1, + qreal y1, qreal r1); + + // rects + void clearRect(qreal x, qreal y, qreal w, qreal h); + void fillRect(qreal x, qreal y, qreal w, qreal h); + void strokeRect(qreal x, qreal y, qreal w, qreal h); + + // mouse + void mouseArea(qreal x, qreal y, qreal w, qreal h, const QScriptValue &, const QScriptValue & = QScriptValue()); + + // path API + void beginPath(); + void closePath(); + void moveTo(qreal x, qreal y); + void lineTo(qreal x, qreal y); + void quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y); + void bezierCurveTo(qreal cp1x, qreal cp1y, + qreal cp2x, qreal cp2y, qreal x, qreal y); + void arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius); + void rect(qreal x, qreal y, qreal w, qreal h); + void arc(qreal x, qreal y, qreal radius, + qreal startAngle, qreal endAngle, + bool anticlockwise); + void fill(); + void stroke(); + void clip(); + bool isPointInPath(qreal x, qreal y) const; + + CanvasImage *createImage(const QString &url); + + // drawing images (no overloads due to QTBUG-11604) + void drawImage(const QVariant &var, qreal dx, qreal dy, qreal dw, qreal dh); + + // pixel manipulation + ImageData getImageData(qreal sx, qreal sy, qreal sw, qreal sh); + void putImageData(ImageData image, qreal dx, qreal dy); + void endPainting(); + +signals: + void changed(); + +private: + void setupPainter(); + void beginPainting(); + void updateShadowBuffer(); + + int m_changeTimerId; + QPainterPath m_path; + + enum DirtyFlag { + DirtyTransformationMatrix = 0x00001, + DirtyClippingRegion = 0x00002, + DirtyStrokeStyle = 0x00004, + DirtyFillStyle = 0x00008, + DirtyGlobalAlpha = 0x00010, + DirtyLineWidth = 0x00020, + DirtyLineCap = 0x00040, + DirtyLineJoin = 0x00080, + DirtyMiterLimit = 0x00100, + MDirtyPen = DirtyStrokeStyle + | DirtyLineWidth + | DirtyLineCap + | DirtyLineJoin + | DirtyMiterLimit, + DirtyShadowOffsetX = 0x00200, + DirtyShadowOffsetY = 0x00400, + DirtyShadowBlur = 0x00800, + DirtyShadowColor = 0x01000, + DirtyGlobalCompositeOperation = 0x2000, + DirtyFont = 0x04000, + DirtyTextAlign = 0x08000, + DirtyTextBaseline = 0x10000, + AllIsFullOfDirt = 0xfffff + }; + + struct State { + State() : flags(0) {} + QMatrix matrix; + QPainterPath clipPath; + QBrush strokeStyle; + QBrush fillStyle; + qreal globalAlpha; + qreal lineWidth; + Qt::PenCapStyle lineCap; + Qt::PenJoinStyle lineJoin; + qreal miterLimit; + qreal shadowOffsetX; + qreal shadowOffsetY; + qreal shadowBlur; + QColor shadowColor; + QPainter::CompositionMode globalCompositeOperation; + QFont font; + Context2D::TextAlign textAlign; + Context2D::TextBaseLine textBaseline; + int flags; + }; + + int baseLineOffset(Context2D::TextBaseLine value, const QFontMetrics &metrics); + int textAlignOffset(Context2D::TextAlign value, const QFontMetrics &metrics, const QString &string); + + QMatrix worldMatrix() const; + + QPoint m_painterTranslate; + State m_state; + QStack m_stateStack; + QPixmap m_pixmap; + QList m_mouseAreas; + QImage m_shadowbuffer; + QVector m_shadowColorIndexBuffer; + QColor m_shadowColorBuffer; + QPainter m_painter; + int m_width, m_height; + bool m_inPaint; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVECONTEXT2D_P_H diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativetiledcanvas.cpp b/src/plugins/qmlprofiler/canvas/qdeclarativetiledcanvas.cpp new file mode 100644 index 00000000000..6cbb96f696a --- /dev/null +++ b/src/plugins/qmlprofiler/canvas/qdeclarativetiledcanvas.cpp @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativetiledcanvas_p.h" + +#include "qdeclarativecontext2d_p.h" + +#include +#include + +TiledCanvas::TiledCanvas() +: m_context2d(new Context2D(this)), m_canvasSize(-1, -1), m_tileSize(100, 100) +{ + setFlag(QGraphicsItem::ItemHasNoContents, false); + setAcceptedMouseButtons(Qt::LeftButton); +} + +QSizeF TiledCanvas::canvasSize() const +{ + return m_canvasSize; +} + +void TiledCanvas::setCanvasSize(const QSizeF &v) +{ + if (m_canvasSize != v) { + m_canvasSize = v; + emit canvasSizeChanged(); + update(); + } +} + +QSize TiledCanvas::tileSize() const +{ + return m_tileSize; +} + +void TiledCanvas::setTileSize(const QSize &v) +{ + if (v != m_tileSize) { + m_tileSize = v; + emit tileSizeChanged(); + update(); + } +} + +QRectF TiledCanvas::canvasWindow() const +{ + return m_canvasWindow; +} + +void TiledCanvas::setCanvasWindow(const QRectF &v) +{ + if (m_canvasWindow != v) { + m_canvasWindow = v; + emit canvasWindowChanged(); + update(); + } +} + +void TiledCanvas::requestPaint() +{ + update(); +} + +void TiledCanvas::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + if (m_context2d->size() != m_tileSize) + m_context2d->setSize(m_tileSize); + + const int tw = m_tileSize.width(); + const int th = m_tileSize.height(); + + int h1 = m_canvasWindow.left() / tw; + int htiles = ((m_canvasWindow.right() - h1 * tw) + tw - 1) / tw; + + int v1 = m_canvasWindow.top() / th; + int vtiles = ((m_canvasWindow.bottom() - v1 * th) + th - 1) / th; + + for (int yy = 0; yy < vtiles; ++yy) { + for (int xx = 0; xx < htiles; ++xx) { + int ht = xx + h1; + int vt = yy + v1; + + m_context2d->reset(); + m_context2d->setPainterTranslate(QPoint(-ht * tw, -vt * th)); + + emit drawRegion(m_context2d, QRect(ht * tw, vt * th, tw, th)); + + p->drawPixmap(-m_canvasWindow.x() + ht * tw, -m_canvasWindow.y() + vt * th, m_context2d->pixmap()); + } + } +} + +void TiledCanvas::componentComplete() +{ + const QMetaObject *metaObject = this->metaObject(); + int propertyCount = metaObject->propertyCount(); + int requestPaintMethod = metaObject->indexOfMethod("requestPaint()"); + for (int ii = TiledCanvas::staticMetaObject.propertyCount(); ii < propertyCount; ++ii) { + QMetaProperty p = metaObject->property(ii); + if (p.hasNotifySignal()) + QMetaObject::connect(this, p.notifySignalIndex(), this, requestPaintMethod, 0, 0); + } + QDeclarativeItem::componentComplete(); +} + + +void TiledCanvas::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_UNUSED(event); + qWarning("MPE"); +} + +QPixmap TiledCanvas::getTile(int xx, int yy) +{ + QPixmap pix(m_tileSize); + + pix.fill(Qt::green); + + QString text = QString::number(xx) + " " + QString::number(yy); + + QPainter p(&pix); + p.drawText(pix.rect(), Qt::AlignHCenter | Qt::AlignVCenter, text); + + return pix; +} + diff --git a/src/plugins/qmlprofiler/canvas/qdeclarativetiledcanvas_p.h b/src/plugins/qmlprofiler/canvas/qdeclarativetiledcanvas_p.h new file mode 100644 index 00000000000..e3f45a700e4 --- /dev/null +++ b/src/plugins/qmlprofiler/canvas/qdeclarativetiledcanvas_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETILEDCANVAS_P_H +#define QDECLARATIVETILEDCANVAS_P_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class Context2D; +class TiledCanvas : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(QSizeF canvasSize READ canvasSize WRITE setCanvasSize NOTIFY canvasSizeChanged) + Q_PROPERTY(QSize tileSize READ tileSize WRITE setTileSize NOTIFY tileSizeChanged) + Q_PROPERTY(QRectF canvasWindow READ canvasWindow WRITE setCanvasWindow NOTIFY canvasWindowChanged) + +public: + TiledCanvas(); + + QSizeF canvasSize() const; + void setCanvasSize(const QSizeF &); + + QSize tileSize() const; + void setTileSize(const QSize &); + + QRectF canvasWindow() const; + void setCanvasWindow(const QRectF &); + +Q_SIGNALS: + void canvasSizeChanged(); + void tileSizeChanged(); + void canvasWindowChanged(); + + void drawRegion(Context2D *ctxt, const QRect ®ion); + +public Q_SLOTS: + void requestPaint(); + +protected: + virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + virtual void componentComplete(); + virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); + +private: + QPixmap getTile(int, int); + + Context2D *m_context2d; + + QSizeF m_canvasSize; + QSize m_tileSize; + QRectF m_canvasWindow; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVETILEDCANVAS_P_H diff --git a/src/plugins/qmlprofiler/lock.png b/src/plugins/qmlprofiler/lock.png new file mode 100644 index 00000000000..4942a5ce58d Binary files /dev/null and b/src/plugins/qmlprofiler/lock.png differ diff --git a/src/plugins/qmlprofiler/mac_p.h b/src/plugins/qmlprofiler/mac_p.h new file mode 100644 index 00000000000..41324c419cf --- /dev/null +++ b/src/plugins/qmlprofiler/mac_p.h @@ -0,0 +1,6 @@ +#ifndef MAC_P_H +#define MAC_P_H + +void disableIntertia(); + +#endif // MAC_P_H diff --git a/src/plugins/qmlprofiler/popup.png b/src/plugins/qmlprofiler/popup.png new file mode 100644 index 00000000000..38028e94324 Binary files /dev/null and b/src/plugins/qmlprofiler/popup.png differ diff --git a/src/plugins/qmlprofiler/qmlprofiler.pro b/src/plugins/qmlprofiler/qmlprofiler.pro new file mode 100644 index 00000000000..9b64be0303b --- /dev/null +++ b/src/plugins/qmlprofiler/qmlprofiler.pro @@ -0,0 +1,45 @@ +TEMPLATE = lib +TARGET = QmlProfiler + +DEFINES += PROFILER_LIBRARY + +include(../../qtcreatorplugin.pri) +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/analyzerbase/analyzerbase.pri) + + +QT += network script opengl declarative + +include(canvas/canvas.pri) +#include($$QMLJSDEBUGGER_PATH/qmljsdebugger-lib.pri) + +SOURCES += \ + qmlprofilerplugin.cpp \ + qmlprofilertool.cpp \ + qmlprofilerengine.cpp \ + tracewindow.cpp \ + timelineview.cpp + +HEADERS += \ + qmlprofilerconstants.h \ + qmlprofiler_global.h \ + qmlprofilerplugin.h \ + qmlprofilertool.h \ + qmlprofilerengine.h \ + tracewindow.h \ + timelineview.h \ + mac_p.h + +RESOURCES += \ + qmlprofiler.qrc + +OTHER_FILES += \ + Detail.qml \ + Elapsed.qml \ + Label.qml \ + MainView.qml \ + RangeDetails.qml \ + RangeMover.qml \ + RecordButton.qml \ + ToolButton.qml \ + MainView.js diff --git a/src/plugins/qmlprofiler/qmlprofiler.qrc b/src/plugins/qmlprofiler/qmlprofiler.qrc new file mode 100644 index 00000000000..f2763e2f2de --- /dev/null +++ b/src/plugins/qmlprofiler/qmlprofiler.qrc @@ -0,0 +1,16 @@ + + + Detail.qml + Elapsed.qml + Label.qml + lock.png + MainView.js + MainView.qml + popup.png + range.png + RangeDetails.qml + RangeMover.qml + RecordButton.qml + ToolButton.qml + + diff --git a/src/plugins/qmlprofiler/qmlprofiler_global.h b/src/plugins/qmlprofiler/qmlprofiler_global.h new file mode 100644 index 00000000000..7a75158cb0c --- /dev/null +++ b/src/plugins/qmlprofiler/qmlprofiler_global.h @@ -0,0 +1,13 @@ +#ifndef QMLPROFILERPLUGIN_GLOBAL_H +#define QMLPROFILERPLUGIN_GLOBAL_H + +#include + +#if defined(QMLPROFILERPLUGIN_LIBRARY) +# define QMLPROFILERPLUGINSHARED_EXPORT Q_DECL_EXPORT +#else +# define QMLPROFILERPLUGINSHARED_EXPORT Q_DECL_IMPORT +#endif + +#endif // QMLPROFILERPLUGIN_GLOBAL_H + diff --git a/src/plugins/qmlprofiler/qmlprofilerconstants.h b/src/plugins/qmlprofiler/qmlprofilerconstants.h new file mode 100644 index 00000000000..36121ef5f46 --- /dev/null +++ b/src/plugins/qmlprofiler/qmlprofilerconstants.h @@ -0,0 +1,14 @@ +#ifndef QMLPROFILERPLUGINCONSTANTS_H +#define QMLPROFILERPLUGINCONSTANTS_H + +namespace QmlProfiler { +namespace Constants { + + const char * const ACTION_ID = "QmlProfilerPlugin.Action"; + const char * const MENU_ID = "QmlProfilerPlugin.Menu"; + +} // namespace QmlProfiler +} // namespace Constants + +#endif // QMLPROFILERPLUGINCONSTANTS_H + diff --git a/src/plugins/qmlprofiler/qmlprofilerengine.cpp b/src/plugins/qmlprofiler/qmlprofilerengine.cpp new file mode 100644 index 00000000000..575e7b0e63f --- /dev/null +++ b/src/plugins/qmlprofiler/qmlprofilerengine.cpp @@ -0,0 +1,126 @@ +#include "qmlprofilerengine.h" + +#include "qmlprofilerplugin.h" +#include "qmlprofilertool.h" + +#include + +#include + +#include "timelineview.h" +#include "tracewindow.h" + +#include + +#include "canvas/qdeclarativecanvas_p.h" +#include "canvas/qdeclarativecontext2d_p.h" +#include "canvas/qdeclarativetiledcanvas_p.h" + +#include +#include +#include "tracewindow.h" + +using namespace Analyzer::Internal; + +class QmlProfilerEngine::QmlProfilerEnginePrivate +{ +public: + QmlProfilerEnginePrivate(QmlProfilerEngine *qq) : q(qq) {} + ~QmlProfilerEnginePrivate() {} + + bool launchperfmonitor(); + + QmlProfilerEngine *q; + + QString m_workingDirectory; + QString m_executable; + QString m_commandLineArguments; + Utils::Environment m_environment; + + QProcess *m_process; + bool m_running; +}; + +QmlProfilerEngine::QmlProfilerEngine(ProjectExplorer::RunConfiguration *runConfiguration) + : IAnalyzerEngine(runConfiguration), d(new QmlProfilerEnginePrivate(this)) +{ + ProjectExplorer::LocalApplicationRunConfiguration *localAppConfig = + qobject_cast(runConfiguration); + + if (!localAppConfig) + return; + + d->m_workingDirectory = localAppConfig->workingDirectory(); + d->m_executable = localAppConfig->executable(); + d->m_commandLineArguments = localAppConfig->commandLineArguments(); + d->m_environment = localAppConfig->environment(); + d->m_process = 0; + d->m_running = false; +} + +QmlProfilerEngine::~QmlProfilerEngine() +{ + if (d->m_running) + stop(); + delete d; +} + +void QmlProfilerEngine::start() +{ + d->launchperfmonitor(); + d->m_running = true; + + emit processRunning(); +} + +void QmlProfilerEngine::stop() +{ + d->m_running = false; + emit stopRecording(); +} + +void QmlProfilerEngine::viewUpdated() +{ + d->m_process->terminate(); + if (!d->m_process->waitForFinished(1000)) { + d->m_process->kill(); + d->m_process->waitForFinished(); + } + + emit processTerminated(); + delete d->m_process; +} + +bool QmlProfilerEngine::QmlProfilerEnginePrivate::launchperfmonitor() +{ + bool qtquick1 = false; + + m_process = new QProcess(); + + QStringList arguments("-qmljsdebugger=port:" + QString::number(QmlProfilerTool::port) + ",block"); + if (QmlProfilerPlugin::debugOutput) + qWarning("QmlProfiler: Launching %s", qPrintable(m_executable)); + + if (qtquick1) { + QProcessEnvironment env; + env.insert("QMLSCENE_IMPORT_NAME", "quick1"); + m_process->setProcessEnvironment(env); + } + + m_process->setProcessChannelMode(QProcess::ForwardedChannels); + m_process->setWorkingDirectory(m_workingDirectory); + m_process->start(m_executable, arguments); + + if (!m_process->waitForStarted()) { + if (QmlProfilerPlugin::debugOutput) + qWarning("QmlProfiler: %s failed to start", qPrintable(m_executable)); + return false; + } + + sleep(1); + + if (QmlProfilerPlugin::debugOutput) + qWarning("QmlProfiler: Connecting to %s:%d", qPrintable(QmlProfilerTool::host), QmlProfilerTool::port); + + return true; +} diff --git a/src/plugins/qmlprofiler/qmlprofilerengine.h b/src/plugins/qmlprofiler/qmlprofilerengine.h new file mode 100644 index 00000000000..a3c5292b742 --- /dev/null +++ b/src/plugins/qmlprofiler/qmlprofilerengine.h @@ -0,0 +1,36 @@ +#ifndef QMLPROFILERENGINE_H +#define QMLPROFILERENGINE_H + +#include "ianalyzerengine.h" + +namespace Analyzer { +namespace Internal { + + +class QmlProfilerEngine : public IAnalyzerEngine +{ + Q_OBJECT +public: + explicit QmlProfilerEngine(ProjectExplorer::RunConfiguration *runConfiguration); + ~QmlProfilerEngine(); + + void start(); + void stop(); + +signals: + void processRunning(); + void processTerminated(); + void stopRecording(); + +public slots: + void viewUpdated(); + +private: + class QmlProfilerEnginePrivate; + QmlProfilerEnginePrivate *d; +}; + +} +} + +#endif // QMLPROFILERENGINE_H diff --git a/src/plugins/qmlprofiler/qmlprofilerplugin.cpp b/src/plugins/qmlprofiler/qmlprofilerplugin.cpp new file mode 100644 index 00000000000..0e6b871c597 --- /dev/null +++ b/src/plugins/qmlprofiler/qmlprofilerplugin.cpp @@ -0,0 +1,93 @@ +#include "qmlprofilerplugin.h" +#include "qmlprofilerconstants.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include "qmlprofilertool.h" + +using namespace Analyzer; +using namespace Analyzer::Internal; + +QmlProfilerPlugin *QmlProfilerPlugin::m_instance = 0; +bool QmlProfilerPlugin::debugOutput = false; + + +class QmlProfilerPlugin::QmlProfilerPluginPrivate +{ +public: + QmlProfilerPluginPrivate(QmlProfilerPlugin *qq): + q(qq) + {} + + void initialize(const QStringList &arguments, QString *errorString); + + QmlProfilerPlugin *q; +}; + +void QmlProfilerPlugin::QmlProfilerPluginPrivate::initialize(const QStringList &arguments, QString *errorString) +{ + Q_UNUSED(arguments) + Q_UNUSED(errorString) + +// AnalyzerManager::instance()->addTool(new MemcheckTool(this)); +} + + + +QmlProfilerPlugin::QmlProfilerPlugin() + : d(new QmlProfilerPluginPrivate(this)) +{ + m_instance = this; +} + +QmlProfilerPlugin::~QmlProfilerPlugin() +{ + delete d; + m_instance = 0; +} + +bool QmlProfilerPlugin::initialize(const QStringList &arguments, QString *errorString) +{ + d->initialize(arguments, errorString); + + AnalyzerManager::instance()->addTool(new QmlProfilerTool(this)); + + return true; +} + +void QmlProfilerPlugin::extensionsInitialized() +{ + // Retrieve objects from the plugin manager's object pool + // "In the extensionsInitialized method, a plugin can be sure that all + // plugins that depend on it are completely initialized." +} + +ExtensionSystem::IPlugin::ShutdownFlag QmlProfilerPlugin::aboutToShutdown() +{ + // Save settings + // Disconnect from signals that are not needed during shutdown + // Hide UI (if you add UI that is not in the main window directly) + return SynchronousShutdown; +} + +QmlProfilerPlugin *QmlProfilerPlugin::instance() +{ + return m_instance; +} + +Q_EXPORT_PLUGIN(QmlProfilerPlugin) + diff --git a/src/plugins/qmlprofiler/qmlprofilerplugin.h b/src/plugins/qmlprofiler/qmlprofilerplugin.h new file mode 100644 index 00000000000..5707c87b18e --- /dev/null +++ b/src/plugins/qmlprofiler/qmlprofilerplugin.h @@ -0,0 +1,38 @@ +#ifndef QMLPROFILERPLUGIN_H +#define QMLPROFILERPLUGIN_H + +#include "qmlprofiler_global.h" + +#include + +namespace Analyzer { +namespace Internal { + +class QmlProfilerPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + static QmlProfilerPlugin *instance(); + + QmlProfilerPlugin(); + ~QmlProfilerPlugin(); + + bool initialize(const QStringList &arguments, QString *errorString); + void extensionsInitialized(); + ShutdownFlag aboutToShutdown(); + + static bool debugOutput; + +private: + class QmlProfilerPluginPrivate; + QmlProfilerPluginPrivate *d; + + static QmlProfilerPlugin *m_instance; +}; + +} // namespace Internal +} // namespace Analyzer + +#endif // QMLPROFILERPLUGIN_H + diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp new file mode 100644 index 00000000000..37aae4c9280 --- /dev/null +++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp @@ -0,0 +1,205 @@ +#include "qmlprofilertool.h" +#include "qmlprofilerengine.h" +#include "qmlprofilerplugin.h" + +#include "tracewindow.h" +#include + +#include +#include + +#include "timelineview.h" + +#include "canvas/qdeclarativecanvas_p.h" +#include "canvas/qdeclarativecontext2d_p.h" +#include "canvas/qdeclarativetiledcanvas_p.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include + + +using namespace Analyzer; +using namespace Analyzer::Internal; + +QString QmlProfilerTool::host = QLatin1String("localhost"); +quint16 QmlProfilerTool::port = 33455; + + +// Adapter for output pane. +class QmlProfilerOutputPaneAdapter : public Analyzer::IAnalyzerOutputPaneAdapter +{ +public: + explicit QmlProfilerOutputPaneAdapter(QmlProfilerTool *mct) : + IAnalyzerOutputPaneAdapter(mct), m_tool(mct) {} + + virtual QWidget *toolBarWidget() { return m_tool->createToolBarWidget(); } + virtual QWidget *paneWidget() { return m_tool->createTimeLineWidget(); } + virtual void clearContents() { /*TODO*/ } + virtual void setFocus() { /*TODO*/ } + virtual bool hasFocus() const { return false; /*TODO*/ } + virtual bool canFocus() const { return false; /*TODO*/ } + virtual bool canNavigate() const { return false; /*TODO*/ } + virtual bool canNext() const { return false; /*TODO*/ } + virtual bool canPrevious() const { return false; /*TODO*/ } + virtual void goToNext() { /*TODO*/ } + virtual void goToPrev() { /*TODO*/ } + + +private: + QmlProfilerTool *m_tool; +}; + +class QmlProfilerTool::QmlProfilerToolPrivate +{ +public: + QmlProfilerToolPrivate(QmlProfilerTool *qq) : q(qq) {} + ~QmlProfilerToolPrivate() {} + + QmlProfilerTool *q; + + QDeclarativeDebugConnection *m_client; + TraceWindow *m_traceWindow; + QmlProfilerOutputPaneAdapter *m_outputPaneAdapter; +}; + +QmlProfilerTool::QmlProfilerTool(QObject *parent) + : IAnalyzerTool(parent), d(new QmlProfilerToolPrivate(this)) +{ + d->m_client = 0; + d->m_traceWindow = 0; + d->m_outputPaneAdapter = 0; +} + +QmlProfilerTool::~QmlProfilerTool() +{ + if (d->m_client->isConnected()) + d->m_client->disconnectFromHost(); + delete d->m_traceWindow; + delete d->m_outputPaneAdapter; + delete d; +} + +QString QmlProfilerTool::id() const +{ + return "QmlProfiler"; +} + +QString QmlProfilerTool::displayName() const +{ + return tr("Qml Performance Monitor"); +} + +IAnalyzerTool::ToolMode QmlProfilerTool::mode() const +{ + return DebugMode; +} + + +IAnalyzerEngine *QmlProfilerTool::createEngine(ProjectExplorer::RunConfiguration *runConfiguration) +{ + QmlProfilerEngine *engine = new QmlProfilerEngine(runConfiguration); + + connect(engine, SIGNAL(processRunning()), this, SLOT(connectClient())); + connect(engine, SIGNAL(processTerminated()), this, SLOT(disconnectClient())); + connect(engine, SIGNAL(stopRecording()), this, SLOT(stopRecording())); + connect(d->m_traceWindow, SIGNAL(viewUpdated()), engine, SLOT(viewUpdated())); + connect(d->m_traceWindow, SIGNAL(gotoSourceLocation(QString,int)), this, SLOT(gotoSourceLocation(QString,int))); + + return engine; + +} + +void QmlProfilerTool::initialize(ExtensionSystem::IPlugin */*plugin*/) +{ + qmlRegisterType("QtQuick",1,1, "Canvas"); + qmlRegisterType("QtQuick",1,1, "TiledCanvas"); + qmlRegisterType(); + qmlRegisterType(); + qmlRegisterType(); + + qmlRegisterType("Monitor",1,0,"TimelineView"); + + d->m_client = new QDeclarativeDebugConnection; + d->m_traceWindow = new TraceWindow(); + d->m_traceWindow->reset(d->m_client); +} + +IAnalyzerOutputPaneAdapter *QmlProfilerTool::outputPaneAdapter() +{ + if (!d->m_outputPaneAdapter) + d->m_outputPaneAdapter = new QmlProfilerOutputPaneAdapter(this); + return d->m_outputPaneAdapter; +} + +QWidget *QmlProfilerTool::createToolBarWidget() +{ + // custom toolbar (TODO) + return 0; +} + +QWidget *QmlProfilerTool::createTimeLineWidget() +{ + return d->m_traceWindow; +} + +void QmlProfilerTool::connectClient() +{ + QDeclarativeDebugConnection *newClient = new QDeclarativeDebugConnection; + d->m_traceWindow->reset(newClient); + delete d->m_client; + d->m_client = newClient; + + d->m_client->connectToHost(host, port); + d->m_client->waitForConnected(); + + if (d->m_client->state() == QDeclarativeDebugConnection::ConnectedState) { + d->m_traceWindow->setRecording(true); + if (QmlProfilerPlugin::debugOutput) + qWarning("QmlProfiler: connected and running"); + } else { + if (QmlProfilerPlugin::debugOutput) + qWarning("QmlProfiler: Failed to connect: %s", qPrintable(d->m_client->errorString())); + } +} + +void QmlProfilerTool::disconnectClient() +{ + d->m_client->disconnectFromHost(); +} + +void QmlProfilerTool::stopRecording() +{ + d->m_traceWindow->setRecording(false); +} + +void QmlProfilerTool::gotoSourceLocation(const QString &fileName, int lineNumber) +{ + if (lineNumber < 0 || !QFile::exists(QUrl(fileName).toLocalFile())) + return; + + Utils::FileInProjectFinder m_projectFinder; + ProjectExplorer::Project *m_debugProject; + m_debugProject = ProjectExplorer::ProjectExplorerPlugin::instance()->startupProject(); + m_projectFinder.setProjectDirectory(m_debugProject->projectDirectory()); + + QString projectFileName = m_projectFinder.findFile(fileName); + + + Core::EditorManager *editorManager = Core::EditorManager::instance(); + Core::IEditor *editor = editorManager->openEditor(projectFileName); + TextEditor::ITextEditor *textEditor = qobject_cast(editor); + + if (textEditor) { + editorManager->addCurrentPositionToNavigationHistory(); + textEditor->gotoLine(lineNumber); + textEditor->widget()->setFocus(); + } +} diff --git a/src/plugins/qmlprofiler/qmlprofilertool.h b/src/plugins/qmlprofiler/qmlprofilertool.h new file mode 100644 index 00000000000..00439a995db --- /dev/null +++ b/src/plugins/qmlprofiler/qmlprofilertool.h @@ -0,0 +1,50 @@ +#ifndef QMLPROFILERTOOL_H +#define QMLPROFILERTOOL_H + +#include "ianalyzertool.h" +#include "ianalyzerengine.h" + +namespace Analyzer { +namespace Internal { + +class QmlProfilerTool : public IAnalyzerTool +{ + Q_OBJECT +public: + explicit QmlProfilerTool(QObject *parent = 0); + ~QmlProfilerTool(); + + QString id() const; + QString displayName() const; + ToolMode mode() const; + + void initialize(ExtensionSystem::IPlugin *plugin); + + IAnalyzerEngine *createEngine(ProjectExplorer::RunConfiguration *runConfiguration); + + IAnalyzerOutputPaneAdapter *outputPaneAdapter(); + QWidget *createToolBarWidget(); + QWidget *createTimeLineWidget(); + +public slots: + void connectClient(); + void disconnectClient(); + + void stopRecording(); + + void gotoSourceLocation(const QString &fileName, int lineNumber); + +public: + // Todo: configurable parameters + static QString host; + static quint16 port; + +private: + class QmlProfilerToolPrivate; + QmlProfilerToolPrivate *d; +}; + +} +} + +#endif // QMLPROFILERTOOL_H diff --git a/src/plugins/qmlprofiler/range.png b/src/plugins/qmlprofiler/range.png new file mode 100644 index 00000000000..2ac200fe18a Binary files /dev/null and b/src/plugins/qmlprofiler/range.png differ diff --git a/src/plugins/qmlprofiler/timelineview.cpp b/src/plugins/qmlprofiler/timelineview.cpp new file mode 100644 index 00000000000..baacd9af186 --- /dev/null +++ b/src/plugins/qmlprofiler/timelineview.cpp @@ -0,0 +1,201 @@ +#include "timelineview.h" + +#include +#include +#include + +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) +{ +} + +void TimelineView::componentComplete() +{ + QDeclarativeItem::componentComplete(); +} + +void TimelineView::clearData() +{ + m_rangeList.clear(); + m_items.clear(); + 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); +} + +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; + qreal spacing = width() / window; + qreal oldStart = m_startTime; + m_startTime = arg / spacing; + m_endTime += m_startTime - oldStart; + + updateTimeline(false); + m_startX = arg; + emit startXChanged(m_startX); +} + +void TimelineView::updateTimeline(bool updateStartX) +{ + if (!m_delegate) + return; + + if (!m_ranges.isArray()) + 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 window = m_endTime - m_startTime; + + if (window == 0) //### + return; + + qreal spacing = width() / window; + qreal oldtw = m_totalWidth; + m_totalWidth = totalRange * spacing; + + qreal offsets[length][2]; + + for (int i = 0; i < length; ++i) { + offsets[i][0] = (m_ranges.property(i).property("start").toNumber() - startValue); + offsets[i][1] = (m_ranges.property(i).property("start").toNumber() + m_ranges.property(i).property("duration").toNumber() - startValue); + } + + // Find region samples + int minsample = 0; + int maxsample = 0; + + for (int i = 0; i < length; ++i) { + if (offsets[i][1] >= m_startTime) + break; + minsample = i; + } + + for (int i = minsample + 1; i < length; ++i) { + maxsample = i; + if (offsets[i][0] > m_endTime) + break; + } + + //### overkill (if we can expose whether or not data is nested) + for (int i = maxsample + 1; i < length; ++i) { + if (offsets[i][0] < m_endTime) + maxsample = i; + } + + //qDebug() << maxsample - minsample; + + if (updateStartX) { + qreal oldStartX = m_startX; + m_startX = qRound(m_startTime * spacing); + if (m_startX != oldStartX) { + emit startXChanged(m_startX); + } + } + + //### emitting this before startXChanged was causing issues + 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-1; 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(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()); + QString label; + QVariantList list = m_ranges.property(i).property("label").toVariant().value(); + for (int i = 0; i < list.size(); ++i) { + if (i > 0) + label += "\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); + } + + label += sub; + } + ctxt->setContextProperty("label", label); + ctxt->setContextProperty("type", type); + item->setY(type*50); + item->setParentItem(this); + } + if (item) { + item->setX(offsets[i][0]*spacing); + item->setWidth(m_ranges.property(i).property("duration").toNumber() * spacing); + item->setZValue(++z); + } + if (creating) + m_delegate->completeCreate(); + } + + prevMin = minsample; + prevMax = maxsample; +} diff --git a/src/plugins/qmlprofiler/timelineview.h b/src/plugins/qmlprofiler/timelineview.h new file mode 100644 index 00000000000..0235d34aec6 --- /dev/null +++ b/src/plugins/qmlprofiler/timelineview.h @@ -0,0 +1,109 @@ +#ifndef TIMELINEVIEW_H +#define TIMELINEVIEW_H + +#include +#include + +class TimelineView : public QDeclarativeItem +{ + Q_OBJECT + Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_PROPERTY(qint64 startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged) + 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) + +public: + explicit TimelineView(QDeclarativeItem *parent = 0); + + QDeclarativeComponent * delegate() const + { + return m_delegate; + } + + qint64 startTime() const + { + return m_startTime; + } + + qint64 endTime() const + { + return m_endTime; + } + + qreal startX() const + { + return m_startX; + } + + qreal totalWidth() const + { + return m_totalWidth; + } + +signals: + void delegateChanged(QDeclarativeComponent * arg); + void startTimeChanged(qint64 arg); + void endTimeChanged(qint64 arg); + void startXChanged(qreal arg); + void totalWidthChanged(qreal arg); + +public slots: + void clearData(); + void setRanges(const QScriptValue &value); + void updateTimeline(bool updateStartX = true); + + void setDelegate(QDeclarativeComponent * arg) + { + if (m_delegate != arg) { + m_delegate = arg; + emit delegateChanged(arg); + } + } + + void setStartTime(qint64 arg) + { + if (m_startTime != arg) { + m_startTime = arg; + emit startTimeChanged(arg); + } + } + + void setEndTime(qint64 arg) + { + if (m_endTime != arg) { + m_endTime = arg; + emit endTimeChanged(arg); + } + } + + void setStartX(qreal arg); + +protected: + void componentComplete(); + +private: + QDeclarativeComponent * m_delegate; + QScriptValue m_ranges; + typedef QList ValueList; + QList m_rangeList; + QHash m_items; + qint64 m_startTime; + qint64 m_endTime; + qreal m_startX; + int prevMin; + int prevMax; + + struct PrevLimits { + PrevLimits(int _min, int _max) : min(_min), max(_max) {} + int min; + int max; + }; + + QList m_prevLimits; + qreal m_totalWidth; +}; + +QML_DECLARE_TYPE(TimelineView) + +#endif // TIMELINEVIEW_H diff --git a/src/plugins/qmlprofiler/tracewindow.cpp b/src/plugins/qmlprofiler/tracewindow.cpp new file mode 100644 index 00000000000..94362327b1b --- /dev/null +++ b/src/plugins/qmlprofiler/tracewindow.cpp @@ -0,0 +1,309 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ +#include "tracewindow.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "qmlprofilerplugin.h" +//#include +//#include + +#define GAP_TIME 150 + +struct Location +{ + Location() : line(-1) {} + Location(const QString &file, int lineNumber) : fileName(file), line(lineNumber) {} + QString fileName; + int line; +}; + +class TracePlugin : public QDeclarativeDebugClient +{ + Q_OBJECT + Q_PROPERTY(bool recording READ recording WRITE setRecording NOTIFY recordingChanged) +public: + TracePlugin(QDeclarativeDebugConnection *client); + + enum EventType { + FramePaint, + Mouse, + Key, + + MaximumEventType + }; + + enum Message { + Event, + RangeStart, + RangeData, + RangeLocation, + RangeEnd, + Complete, + + MaximumMessage + }; + + enum RangeType { + Painting, + Compiling, + Creating, + Binding, + HandlingSignal, + + MaximumRangeType + }; + + bool recording() const + { + return m_recording; + } + +public slots: + void setRecording(bool); + +signals: + void complete(); + void gap(qint64); + void event(int event, qint64 time); + void range(int type, qint64 startTime, qint64 length, const QStringList &data, const QString &fileName, int line); + + void sample(int, int, int, bool); + + void recordingChanged(bool arg); + + void enabled(); + +protected: + virtual void statusChanged(Status); + virtual void messageReceived(const QByteArray &); + +private: + + qint64 m_inProgressRanges; + QStack m_rangeStartTimes[MaximumRangeType]; + QStack m_rangeDatas[MaximumRangeType]; + QStack m_rangeLocations[MaximumRangeType]; + int m_rangeCount[MaximumRangeType]; + qint64 m_maximumTime; + bool m_recording; +}; + +TracePlugin::TracePlugin(QDeclarativeDebugConnection *client) + : QDeclarativeDebugClient(QLatin1String("CanvasFrameRate"), client), m_inProgressRanges(0), m_maximumTime(0), m_recording(false) +{ + ::memset(m_rangeCount, 0, MaximumRangeType * sizeof(int)); +} + +void TracePlugin::setRecording(bool v) +{ + if (v == m_recording) + return; + QByteArray ba; + QDataStream stream(&ba, QIODevice::WriteOnly); + stream << v; + sendMessage(ba); + m_recording = v; + emit recordingChanged(v); +} + +void TracePlugin::statusChanged(Status status) +{ + if (status == Enabled) { + m_recording = !m_recording; + setRecording(!m_recording); + emit enabled(); + } +} + +void TracePlugin::messageReceived(const QByteArray &data) +{ + QByteArray rwData = data; + QDataStream stream(&rwData, QIODevice::ReadOnly); + + qint64 time; + int messageType; + + stream >> time >> messageType; + +// qDebug() << __FUNCTION__ << messageType; + + if (messageType >= MaximumMessage) + return; + + if (time > (m_maximumTime + GAP_TIME) && 0 == m_inProgressRanges) + emit gap(time); + + if (messageType == Event) { + int event; + stream >> event; + + if (event < MaximumEventType) { + emit this->event((EventType)event, time); + m_maximumTime = qMax(time, m_maximumTime); + } + } else if (messageType == Complete) { + emit complete(); + } else { + int range; + stream >> range; + + if (range >= MaximumRangeType) + return; + + if (messageType == RangeStart) { + m_rangeStartTimes[range].push(time); + m_inProgressRanges |= (1 << range); + ++m_rangeCount[range]; + } else if (messageType == RangeData) { + QString data; + stream >> data; + + int count = m_rangeCount[range]; + if (count > 0) { + while (m_rangeDatas[range].count() < count) + m_rangeDatas[range].push(QStringList()); + m_rangeDatas[range][count-1] << data; + } + + } else if (messageType == RangeLocation) { + QString fileName; + int line; + stream >> fileName >> line; + + if (m_rangeCount[range] > 0) { + m_rangeLocations[range].push(Location(fileName, line)); + } + } else { + if (m_rangeCount[range] > 0) { + --m_rangeCount[range]; + if (m_inProgressRanges & (1 << range)) + m_inProgressRanges &= ~(1 << range); + + m_maximumTime = qMax(time, m_maximumTime); + QStringList data = m_rangeDatas[range].count() ? m_rangeDatas[range].pop() : QStringList(); + Location location = m_rangeLocations[range].count() ? m_rangeLocations[range].pop() : Location(); + + qint64 startTime = m_rangeStartTimes[range].pop(); + emit this->range((RangeType)range, startTime, time - startTime, data, location.fileName, location.line); + if (m_rangeCount[range] == 0) { + int count = m_rangeDatas[range].count() + + m_rangeStartTimes[range].count() + + m_rangeLocations[range].count(); + if (count != 0) + qWarning() << "incorrectly nested data"; + } + } + } + } +} + +TraceWindow::TraceWindow(QWidget *parent) +: QWidget(parent), + m_plugin(0), m_recordAtStart(false) +{ + setObjectName(tr("Qml Perfomance Monitor")); + + QVBoxLayout *groupLayout = new QVBoxLayout; + groupLayout->setContentsMargins(0, 0, 0, 0); + groupLayout->setSpacing(0); + + m_view = new QDeclarativeView(this); + + if (Analyzer::Internal::QmlProfilerPlugin::debugOutput) { + //new QmlJSDebugger::JSDebuggerAgent(m_view->engine()); + //new QmlJSDebugger::QDeclarativeViewObserver(m_view, m_view); + } + + m_view->setViewport(new QGLWidget()); + m_view->setResizeMode(QDeclarativeView::SizeRootObjectToView); + m_view->setFocus(); + groupLayout->addWidget(m_view); + + setLayout(groupLayout); + + // Maximum height: 5 rows of 50 pixels + scrollbar of 50 pixels + setMinimumHeight(300); + setMaximumHeight(300); +} + +TraceWindow::~TraceWindow() +{ + delete m_plugin; +} + +void TraceWindow::reset(QDeclarativeDebugConnection *conn) +{ + if (m_plugin) + disconnect(m_plugin,SIGNAL(complete()), this, SIGNAL(viewUpdated())); + delete m_plugin; + m_plugin = new TracePlugin(conn); + connect(m_plugin,SIGNAL(complete()), this, SIGNAL(viewUpdated())); + if (m_recordAtStart) + m_plugin->setRecording(true); + + m_view->rootContext()->setContextProperty("connection", m_plugin); + m_view->setSource(QUrl("qrc:/qml/MainView.qml")); + + connect(m_view->rootObject(), SIGNAL(updateCursorPosition()), this, SLOT(updateCursorPosition())); +} + +void TraceWindow::updateCursorPosition() +{ + emit gotoSourceLocation(m_view->rootObject()->property("fileName").toString(), + m_view->rootObject()->property("lineNumber").toInt()); +} + +void TraceWindow::setRecordAtStart(bool record) +{ + m_recordAtStart = record; +} + +void TraceWindow::setRecording(bool recording) +{ + m_plugin->setRecording(recording); +} + +#include "tracewindow.moc" diff --git a/src/plugins/qmlprofiler/tracewindow.h b/src/plugins/qmlprofiler/tracewindow.h new file mode 100644 index 00000000000..813be6a7381 --- /dev/null +++ b/src/plugins/qmlprofiler/tracewindow.h @@ -0,0 +1,75 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ +#ifndef CANVASFRAMERATE_H +#define CANVASFRAMERATE_H + +#include + +#include +#include + +class QTabWidget; +class QSlider; +class QGroupBox; +class QLabel; +class QSpinBox; +class QPushButton; +class QDeclarativeView; + +class TracePlugin; + +class TraceWindow : public QWidget +{ + Q_OBJECT +public: + TraceWindow(QWidget *parent = 0); + ~TraceWindow(); + + void reset(QDeclarativeDebugConnection *conn); + void setRecordAtStart(bool record); + + void setRecording(bool recording); + +public slots: + void updateCursorPosition(); + +signals: + void viewUpdated(); + void gotoSourceLocation(const QString &fileName, int lineNumber); + +private: + TracePlugin *m_plugin; + QSize m_sizeHint; + bool m_recordAtStart; + + QDeclarativeView *m_view; +}; + +#endif // CANVASFRAMERATE_H +