/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #include "timelinerenderer.h" #include #include #include #include #include #include #include #include using namespace QmlProfiler::Internal; const int DefaultRowHeight = 30; TimelineRenderer::TimelineRenderer(QQuickPaintedItem *parent) : QQuickPaintedItem(parent), m_startTime(0), m_endTime(0), m_spacing(0), m_lastStartTime(0), m_lastEndTime(0) , m_profilerModelProxy(0) { clearData(); setAcceptedMouseButtons(Qt::LeftButton); setAcceptHoverEvents(true); } void TimelineRenderer::setProfilerModelProxy(QObject *profilerModelProxy) { if (m_profilerModelProxy) disconnect(m_profilerModelProxy, SIGNAL(expandedChanged()), this, SLOT(requestPaint())); m_profilerModelProxy = qobject_cast(profilerModelProxy); if (m_profilerModelProxy) connect(m_profilerModelProxy, SIGNAL(expandedChanged()), this, SLOT(requestPaint())); emit profilerModelProxyChanged(m_profilerModelProxy); } void TimelineRenderer::componentComplete() { const QMetaObject *metaObject = this->metaObject(); int propertyCount = metaObject->propertyCount(); int requestPaintMethod = metaObject->indexOfMethod("requestPaint()"); for (int ii = TimelineRenderer::staticMetaObject.propertyCount(); ii < propertyCount; ++ii) { QMetaProperty p = metaObject->property(ii); if (p.hasNotifySignal()) QMetaObject::connect(this, p.notifySignalIndex(), this, requestPaintMethod, 0, 0); } QQuickItem::componentComplete(); } void TimelineRenderer::requestPaint() { update(); } void TimelineRenderer::paint(QPainter *p) { qint64 windowDuration = m_endTime - m_startTime; if (windowDuration <= 0) return; m_spacing = qreal(width()) / windowDuration; p->setPen(Qt::transparent); for (int modelIndex = 0; modelIndex < m_profilerModelProxy->modelCount(); modelIndex++) { int lastIndex = m_profilerModelProxy->findLastIndex(modelIndex, m_endTime); if (lastIndex >= 0 && lastIndex < m_profilerModelProxy->count(modelIndex)) { int firstIndex = m_profilerModelProxy->findFirstIndex(modelIndex, m_startTime); if (firstIndex >= 0) { drawItemsToPainter(p, modelIndex, firstIndex, lastIndex); if (m_selectedModel == modelIndex) drawSelectionBoxes(p, modelIndex, firstIndex, lastIndex); drawBindingLoopMarkers(p, modelIndex, firstIndex, lastIndex); } } } m_lastStartTime = m_startTime; m_lastEndTime = m_endTime; } void TimelineRenderer::drawItemsToPainter(QPainter *p, int modelIndex, int fromIndex, int toIndex) { p->save(); p->setPen(Qt::transparent); int modelRowStart = 0; for (int mi = 0; mi < modelIndex; mi++) modelRowStart += m_profilerModelProxy->rowCount(mi); for (int i = fromIndex; i <= toIndex; i++) { int currentX, currentY, itemWidth, itemHeight; currentX = (m_profilerModelProxy->getStartTime(modelIndex, i) - m_startTime) * m_spacing; int rowNumber = m_profilerModelProxy->getEventRow(modelIndex, i); currentY = (modelRowStart + rowNumber) * DefaultRowHeight - y(); if (currentY >= height()) continue; itemWidth = m_profilerModelProxy->getDuration(modelIndex, i) * m_spacing; if (itemWidth < 1) itemWidth = 1; itemHeight = DefaultRowHeight * m_profilerModelProxy->getHeight(modelIndex, i); currentY += DefaultRowHeight - itemHeight; if (currentY + itemHeight < 0) continue; // normal events p->setBrush(m_profilerModelProxy->getColor(modelIndex, i)); p->drawRect(currentX, currentY, itemWidth, itemHeight); } p->restore(); } void TimelineRenderer::drawSelectionBoxes(QPainter *p, int modelIndex, int fromIndex, int toIndex) { if (m_selectedItem == -1) return; int id = m_profilerModelProxy->getEventId(modelIndex, m_selectedItem); int modelRowStart = 0; for (int mi = 0; mi < modelIndex; mi++) modelRowStart += m_profilerModelProxy->rowCount(mi); p->save(); QColor selectionColor = Qt::blue; if (m_selectionLocked) selectionColor = QColor(96,0,255); QPen strongPen(selectionColor, 3); QPen lightPen(QBrush(selectionColor.lighter(130)), 2); lightPen.setJoinStyle(Qt::MiterJoin); p->setPen(lightPen); p->setBrush(Qt::transparent); int currentX, currentY, itemWidth; QRect selectedItemRect(0,0,0,0); for (int i = fromIndex; i <= toIndex; i++) { if (m_profilerModelProxy->getEventId(modelIndex, i) != id) continue; currentX = (m_profilerModelProxy->getStartTime(modelIndex, i) - m_startTime) * m_spacing; currentY = (modelRowStart + m_profilerModelProxy->getEventRow(modelIndex, i)) * DefaultRowHeight - y(); if (currentY + DefaultRowHeight < 0 || height() < currentY) continue; itemWidth = m_profilerModelProxy->getDuration(modelIndex, i) * m_spacing; if (itemWidth < 1) itemWidth = 1; if (i == m_selectedItem) selectedItemRect = QRect(currentX, currentY - 1, itemWidth, DefaultRowHeight + 1); else p->drawRect(currentX, currentY, itemWidth, DefaultRowHeight); } // draw the selected item rectangle the last, so that it's overlayed if (selectedItemRect.width() != 0) { p->setPen(strongPen); p->drawRect(selectedItemRect); } p->restore(); } void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int modelIndex, int fromIndex, int toIndex) { int destindex; int xfrom, xto; int yfrom, yto; int radius = DefaultRowHeight / 3; QPen shadowPen = QPen(QColor("grey"),2); QPen markerPen = QPen(QColor("orange"),2); QBrush shadowBrush = QBrush(QColor("grey")); QBrush markerBrush = QBrush(QColor("orange")); p->save(); for (int i = fromIndex; i <= toIndex; i++) { destindex = m_profilerModelProxy->getBindingLoopDest(modelIndex, i); if (destindex >= 0) { // from xfrom = (m_profilerModelProxy->getStartTime(modelIndex, i) + m_profilerModelProxy->getDuration(modelIndex, i)/2 - m_startTime) * m_spacing; yfrom = getYPosition(modelIndex, i) + DefaultRowHeight / 2 - y(); // to xto = (m_profilerModelProxy->getStartTime(modelIndex, destindex) + m_profilerModelProxy->getDuration(modelIndex, destindex)/2 - m_startTime) * m_spacing; yto = getYPosition(modelIndex, destindex) + DefaultRowHeight / 2 - y(); // radius int eventWidth = m_profilerModelProxy->getDuration(modelIndex, i) * m_spacing; radius = 5; if (radius * 2 > eventWidth) radius = eventWidth / 2; if (radius < 2) radius = 2; int shadowoffset = 2; if ((yfrom + radius + shadowoffset < 0 && yto + radius + shadowoffset < 0) || (yfrom - radius >= height() && yto - radius >= height())) return; // shadow p->setPen(shadowPen); p->setBrush(shadowBrush); p->drawEllipse(QPoint(xfrom, yfrom + shadowoffset), radius, radius); p->drawEllipse(QPoint(xto, yto + shadowoffset), radius, radius); p->drawLine(QPoint(xfrom, yfrom + shadowoffset), QPoint(xto, yto + shadowoffset)); // marker p->setPen(markerPen); p->setBrush(markerBrush); p->drawEllipse(QPoint(xfrom, yfrom), radius, radius); p->drawEllipse(QPoint(xto, yto), radius, radius); p->drawLine(QPoint(xfrom, yfrom), QPoint(xto, yto)); } } p->restore(); } int TimelineRenderer::modelFromPosition(int y) { y = y / DefaultRowHeight; for (int modelIndex = 0; modelIndex < m_profilerModelProxy->modelCount(); modelIndex++) { y -= m_profilerModelProxy->rowCount(modelIndex); if (y < 0) return modelIndex; } return 0; } void TimelineRenderer::mousePressEvent(QMouseEvent *event) { // special case: if there is a drag area below me, don't accept the // events unless I'm actually clicking inside an item if (m_currentSelection.eventIndex == -1 && event->pos().x()+x() >= m_startDragArea && event->pos().x()+x() <= m_endDragArea) event->setAccepted(false); } void TimelineRenderer::mouseReleaseEvent(QMouseEvent *event) { Q_UNUSED(event); manageClicked(); } void TimelineRenderer::mouseMoveEvent(QMouseEvent *event) { event->setAccepted(false); } void TimelineRenderer::hoverMoveEvent(QHoverEvent *event) { Q_UNUSED(event); manageHovered(event->pos().x(), event->pos().y()); if (m_currentSelection.eventIndex == -1) event->setAccepted(false); } void TimelineRenderer::manageClicked() { if (m_currentSelection.eventIndex != -1) { if (m_currentSelection.eventIndex == m_selectedItem && m_currentSelection.modelIndex == m_selectedModel) setSelectionLocked(!m_selectionLocked); else setSelectionLocked(true); emit itemPressed(m_currentSelection.modelIndex, m_currentSelection.eventIndex); } else { setSelectionLocked(false); } setSelectedModel(m_currentSelection.modelIndex); setSelectedItem(m_currentSelection.eventIndex); } void TimelineRenderer::manageHovered(int mouseX, int mouseY) { if (m_endTime - m_startTime <=0 || m_lastEndTime - m_lastStartTime <= 0) return; qint64 time = mouseX * (m_endTime - m_startTime) / width() + m_startTime; int row = (mouseY + y()) / DefaultRowHeight; int modelIndex = modelFromPosition(mouseY + y()); // already covered? nothing to do if (m_currentSelection.eventIndex != -1 && time >= m_currentSelection.startTime && time <= m_currentSelection.endTime && row == m_currentSelection.row) { return; } // find if there's items in the time range int eventFrom = m_profilerModelProxy->findFirstIndex(modelIndex, time); int eventTo = m_profilerModelProxy->findLastIndex(modelIndex, time); if (eventFrom == -1 || eventTo < eventFrom || eventTo >= m_profilerModelProxy->count()) { m_currentSelection.eventIndex = -1; return; } int modelRowStart = 0; for (int mi = 0; mi < modelIndex; mi++) modelRowStart += m_profilerModelProxy->rowCount(mi); // find if we are in the right column int itemRow; for (int i=eventTo; i>=eventFrom; --i) { if (ceil(m_profilerModelProxy->getEndTime(modelIndex, i)*m_spacing) < floor(time*m_spacing)) continue; itemRow = modelRowStart + m_profilerModelProxy->getEventRow(modelIndex, i); if (itemRow == row) { // match m_currentSelection.eventIndex = i; m_currentSelection.startTime = m_profilerModelProxy->getStartTime(modelIndex, i); m_currentSelection.endTime = m_profilerModelProxy->getEndTime(modelIndex, i); m_currentSelection.row = row; m_currentSelection.modelIndex = modelIndex; if (!m_selectionLocked) { setSelectedModel(modelIndex); setSelectedItem(i); } return; } } m_currentSelection.eventIndex = -1; return; } void TimelineRenderer::clearData() { m_startTime = 0; m_endTime = 0; m_lastStartTime = 0; m_lastEndTime = 0; m_currentSelection.startTime = -1; m_currentSelection.endTime = -1; m_currentSelection.row = -1; m_currentSelection.eventIndex = -1; m_currentSelection.modelIndex = -1; m_selectedItem = -1; m_selectedModel = -1; m_selectionLocked = true; } int TimelineRenderer::getYPosition(int modelIndex, int index) const { Q_ASSERT(m_profilerModelProxy); if (index >= m_profilerModelProxy->count()) return 0; int modelRowStart = 0; for (int mi = 0; mi < modelIndex; mi++) modelRowStart += m_profilerModelProxy->rowCount(mi); int y = DefaultRowHeight * (modelRowStart + m_profilerModelProxy->getEventRow(modelIndex, index)); return y; } void TimelineRenderer::selectNext() { if (m_profilerModelProxy->count() == 0) return; qint64 searchTime = m_startTime; if (m_selectedItem != -1) searchTime = m_profilerModelProxy->getStartTime(m_selectedModel, m_selectedItem); QVarLengthArray itemIndexes(m_profilerModelProxy->modelCount()); for (int i = 0; i < m_profilerModelProxy->modelCount(); i++) { if (m_profilerModelProxy->count(i) > 0) { if (m_selectedModel == i) { itemIndexes[i] = (m_selectedItem + 1) % m_profilerModelProxy->count(i); } else { if (m_profilerModelProxy->getStartTime(i, 0) > searchTime) itemIndexes[i] = 0; else itemIndexes[i] = (m_profilerModelProxy->findLastIndex(i, searchTime) + 1) % m_profilerModelProxy->count(i); } } else { itemIndexes[i] = -1; } } int candidateModelIndex = -1; qint64 candidateStartTime = m_profilerModelProxy->traceEndTime(); for (int i = 0; i < m_profilerModelProxy->modelCount(); i++) { if (itemIndexes[i] == -1) continue; qint64 newStartTime = m_profilerModelProxy->getStartTime(i, itemIndexes[i]); if (newStartTime > searchTime && newStartTime < candidateStartTime) { candidateStartTime = newStartTime; candidateModelIndex = i; } } int itemIndex; if (candidateModelIndex != -1) { itemIndex = itemIndexes[candidateModelIndex]; } else { // find the first index of them all (todo: the modelproxy should do this) itemIndex = -1; candidateStartTime = m_profilerModelProxy->traceEndTime(); for (int i = 0; i < m_profilerModelProxy->modelCount(); i++) if (m_profilerModelProxy->count(i) > 0 && m_profilerModelProxy->getStartTime(i,0) < candidateStartTime) { candidateModelIndex = i; itemIndex = 0; candidateStartTime = m_profilerModelProxy->getStartTime(i,0); } } setSelectedModel(candidateModelIndex); setSelectedItem(itemIndex); } void TimelineRenderer::selectPrev() { if (m_profilerModelProxy->count() == 0) return; qint64 searchTime = m_endTime; if (m_selectedItem != -1) searchTime = m_profilerModelProxy->getEndTime(m_selectedModel, m_selectedItem); QVarLengthArray itemIndexes(m_profilerModelProxy->modelCount()); for (int i = 0; i < m_profilerModelProxy->modelCount(); i++) { if (m_selectedModel == i) { itemIndexes[i] = m_selectedItem - 1; if (itemIndexes[i] < 0) itemIndexes[i] = m_profilerModelProxy->count(m_selectedModel) -1; } else itemIndexes[i] = m_profilerModelProxy->findLastIndex(i, searchTime); } int candidateModelIndex = -1; qint64 candidateStartTime = m_profilerModelProxy->traceStartTime(); for (int i = 0; i < m_profilerModelProxy->modelCount(); i++) { if (itemIndexes[i] == -1 || itemIndexes[i] >= m_profilerModelProxy->count(i)) continue; qint64 newStartTime = m_profilerModelProxy->getStartTime(i, itemIndexes[i]); if (newStartTime < searchTime && newStartTime > candidateStartTime) { candidateStartTime = newStartTime; candidateModelIndex = i; } } int itemIndex = -1; if (candidateModelIndex != -1) { itemIndex = itemIndexes[candidateModelIndex]; } else { // find the last index of them all (todo: the modelproxy should do this) candidateModelIndex = 0; candidateStartTime = m_profilerModelProxy->traceStartTime(); for (int i = 0; i < m_profilerModelProxy->modelCount(); i++) if (m_profilerModelProxy->count(i) > 0 && m_profilerModelProxy->getStartTime(i,m_profilerModelProxy->count(i)-1) > candidateStartTime) { candidateModelIndex = i; itemIndex = m_profilerModelProxy->count(candidateModelIndex) - 1; candidateStartTime = m_profilerModelProxy->getStartTime(i,m_profilerModelProxy->count(i)-1); } } setSelectedModel(candidateModelIndex); setSelectedItem(itemIndex); } int TimelineRenderer::nextItemFromId(int modelIndex, int eventId) const { int ndx = -1; if (m_selectedItem == -1) ndx = m_profilerModelProxy->findFirstIndexNoParents(modelIndex, m_startTime); else ndx = m_selectedItem + 1; if (ndx < 0) return -1; if (ndx >= m_profilerModelProxy->count(modelIndex)) ndx = 0; int startIndex = ndx; do { if (m_profilerModelProxy->getEventId(modelIndex, ndx) == eventId) return ndx; ndx = (ndx + 1) % m_profilerModelProxy->count(modelIndex); } while (ndx != startIndex); return -1; } int TimelineRenderer::prevItemFromId(int modelIndex, int eventId) const { int ndx = -1; if (m_selectedItem == -1) ndx = m_profilerModelProxy->findFirstIndexNoParents(modelIndex, m_startTime); else ndx = m_selectedItem - 1; if (ndx < 0) ndx = m_profilerModelProxy->count(modelIndex) - 1; int startIndex = ndx; do { if (m_profilerModelProxy->getEventId(modelIndex, ndx) == eventId) return ndx; if (--ndx < 0) ndx = m_profilerModelProxy->count(modelIndex)-1; } while (ndx != startIndex); return -1; } void TimelineRenderer::selectNextFromId(int modelIndex, int eventId) { int eventIndex = nextItemFromId(modelIndex, eventId); if (eventIndex != -1) { setSelectedModel(modelIndex); setSelectedItem(eventIndex); } } void TimelineRenderer::selectPrevFromId(int modelIndex, int eventId) { int eventIndex = prevItemFromId(modelIndex, eventId); if (eventIndex != -1) { setSelectedModel(modelIndex); setSelectedItem(eventIndex); } }