/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at info@qt.nokia.com. ** **************************************************************************/ #include "timelineview.h" #include #include #include #include #include #include #include using namespace QmlProfiler::Internal; const int DefaultRowHeight = 30; TimelineView::TimelineView(QDeclarativeItem *parent) : QDeclarativeItem(parent), m_startTime(0), m_endTime(0), m_spacing(0), m_lastStartTime(0), m_lastEndTime(0), m_eventList(0) { clearData(); setFlag(QGraphicsItem::ItemHasNoContents, false); setAcceptedMouseButtons(Qt::LeftButton); setAcceptHoverEvents(true); for (int i=0; imetaObject(); int propertyCount = metaObject->propertyCount(); int requestPaintMethod = metaObject->indexOfMethod("requestPaint()"); for (int ii = TimelineView::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 TimelineView::requestPaint() { update(); } void TimelineView::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) { qint64 windowDuration = m_endTime - m_startTime; if (windowDuration <= 0) return; m_spacing = qreal(width()) / windowDuration; m_rowWidths.clear(); for (int i=0; iuniqueEventsOfType(i) : m_eventList->maxNestingForType(i)); } // event rows m_rowStarts.clear(); int pos = 0; for (int i=0; isetPen(Qt::transparent); // speedup: don't draw overlapping events, just skip them m_rowLastX.clear(); for (int i=0; ifindFirstIndex(m_startTime); int lastIndex = m_eventList->findLastIndex(m_endTime); drawItemsToPainter(p, firstIndex, lastIndex); drawSelectionBoxes(p); m_lastStartTime = m_startTime; m_lastEndTime = m_endTime; } QColor TimelineView::colorForItem(int itemIndex) { int ndx = m_eventList->getHash(itemIndex); return QColor::fromHsl((ndx*25)%360, 76, 166); } QLinearGradient *TimelineView::gradientForItem(int itemIndex) { int ndx = m_eventList->getHash(itemIndex); if (!m_hashedGradients.contains(ndx)) { QLinearGradient *linearGrad = new QLinearGradient(0,0,0,DefaultRowHeight); linearGrad->setColorAt(0, colorForItem(itemIndex)); linearGrad->setColorAt(0.5, colorForItem(itemIndex).darker(115)); linearGrad->setColorAt(1, colorForItem(itemIndex)); m_hashedGradients[ndx] = linearGrad; } return m_hashedGradients[ndx]; } void TimelineView::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex) { int x,y,width,rowNumber, eventType; for (int i = fromIndex; i <= toIndex; i++) { x = (m_eventList->getStartTime(i) - m_startTime) * m_spacing; eventType = m_eventList->getType(i); if (m_rowsExpanded[eventType]) y = m_rowStarts[eventType] + DefaultRowHeight*m_eventList->eventPosInType(i); else y = m_rowStarts[eventType] + DefaultRowHeight*(m_eventList->getNestingLevel(i)-1); width = m_eventList->getDuration(i)*m_spacing; if (width<1) width = 1; rowNumber = y/DefaultRowHeight; if (m_rowLastX[rowNumber] > x+width) continue; m_rowLastX[rowNumber] = x+width; p->setBrush(*gradientForItem(i)); p->drawRect(x,y,width,DefaultRowHeight); } } void TimelineView::drawSelectionBoxes(QPainter *p) { if (m_selectedItem == -1) return; int fromIndex = m_eventList->findFirstIndex(m_startTime); int toIndex = m_eventList->findLastIndex(m_endTime); int id = m_eventList->getHash(m_selectedItem); p->setBrush(Qt::transparent); QPen strongPen(QBrush(Qt::blue), 3); QPen lightPen(QBrush(QColor(Qt::blue).lighter(130)), 2); p->setPen(lightPen); int x, y, width, eventType; for (int i = fromIndex; i <= toIndex; i++) { if (m_eventList->getHash(i) != id) continue; if (i == m_selectedItem) p->setPen(strongPen); else p->setPen(lightPen); x = (m_eventList->getStartTime(i) - m_startTime) * m_spacing; eventType = m_eventList->getType(i); if (m_rowsExpanded[eventType]) y = m_rowStarts[eventType] + DefaultRowHeight*m_eventList->eventPosInType(i); else y = m_rowStarts[eventType] + DefaultRowHeight*(m_eventList->getNestingLevel(i)-1); width = m_eventList->getDuration(i)*m_spacing; if (width<1) width = 1; p->drawRect(x,y,width,DefaultRowHeight); } } void TimelineView::mousePressEvent(QGraphicsSceneMouseEvent *event) { // special case: if there is a drag area below me, don't accept the // events unless I'm actually clicking inside an item if (m_currentSelection.eventIndex == -1 && event->pos().x()+x() >= m_startDragArea && event->pos().x()+x() <= m_endDragArea) event->setAccepted(false); } void TimelineView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { Q_UNUSED(event); manageClicked(); } void TimelineView::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { event->setAccepted(false); } void TimelineView::hoverMoveEvent(QGraphicsSceneHoverEvent *event) { Q_UNUSED(event); manageHovered(event->pos().x(), event->pos().y()); if (m_currentSelection.eventIndex == -1) event->setAccepted(false); } void TimelineView::manageClicked() { if (m_currentSelection.eventIndex != -1) { if (m_currentSelection.eventIndex == m_selectedItem) setSelectionLocked(!m_selectionLocked); else setSelectionLocked(true); emit itemPressed(m_currentSelection.eventIndex); } else { // setSelectionLocked(false); } setSelectedItem(m_currentSelection.eventIndex); } void TimelineView::manageHovered(int x, int y) { if (m_endTime - m_startTime <=0) return; qint64 time = x * (m_endTime - m_startTime) / width() + m_startTime; int row = y / DefaultRowHeight; // already covered? nothing to do if (m_currentSelection.eventIndex != -1 && time >= m_currentSelection.startTime && time <= m_currentSelection.endTime && row == m_currentSelection.row) { return; } // find if there's items in the time range int eventFrom = m_eventList->findFirstIndex(time); int eventTo = m_eventList->findLastIndex(time); if (eventTo < eventFrom) { m_currentSelection.eventIndex = -1; return; } // find if we are in the right column int itemRow, eventType; for (int i=eventTo; i>=eventFrom; --i) { if (ceil(m_eventList->getEndTime(i)*m_spacing) < floor(time*m_spacing)) continue; eventType = m_eventList->getType(i); if (m_rowsExpanded[eventType]) itemRow = m_rowStarts[eventType]/DefaultRowHeight + m_eventList->eventPosInType(i); else itemRow = m_rowStarts[eventType]/DefaultRowHeight + m_eventList->getNestingLevel(i)-1; if (itemRow == row) { // match m_currentSelection.eventIndex = i; m_currentSelection.startTime = m_eventList->getStartTime(i); m_currentSelection.endTime = m_eventList->getEndTime(i); m_currentSelection.row = row; if (!m_selectionLocked) setSelectedItem(i); return; } } m_currentSelection.eventIndex = -1; return; } void TimelineView::clearData() { m_startTime = 0; m_endTime = 0; m_lastStartTime = 0; m_lastEndTime = 0; m_currentSelection.startTime = -1; m_currentSelection.endTime = -1; m_currentSelection.row = -1; m_currentSelection.eventIndex = -1; m_selectedItem = -1; m_selectionLocked = true; } qint64 TimelineView::getDuration(int index) const { Q_ASSERT(m_eventList); return m_eventList->getEndTime(index) - m_eventList->getStartTime(index); } QString TimelineView::getFilename(int index) const { Q_ASSERT(m_eventList); return m_eventList->getFilename(index); } int TimelineView::getLine(int index) const { Q_ASSERT(m_eventList); return m_eventList->getLine(index); } QString TimelineView::getDetails(int index) const { Q_ASSERT(m_eventList); return m_eventList->getDetails(index); } void TimelineView::rowExpanded(int rowIndex, bool expanded) { m_rowsExpanded[rowIndex] = expanded; update(); } void TimelineView::selectNext() { if (m_eventList->count() == 0) return; if (m_selectionLocked && m_selectedItem !=-1 ) { // find next item with same hashId int hashId = m_eventList->getHash(m_selectedItem); int i = m_selectedItem+1; while (icount() && m_eventList->getHash(i) != hashId) i++; if (i == m_eventList->count()) { i = 0; while (igetHash(i) != hashId) i++; } setSelectedItem(i); } else { // select next in view or after int newIndex = m_selectedItem+1; if (newIndex >= m_eventList->count()) newIndex = 0; if (m_eventList->getEndTime(newIndex) < m_startTime) newIndex = m_eventList->findFirstIndexNoParents(m_startTime); setSelectedItem(newIndex); } } void TimelineView::selectPrev() { if (m_eventList->count() == 0) return; if (m_selectionLocked && m_selectedItem !=-1) { // find previous item with same hashId int hashId = m_eventList->getHash(m_selectedItem); int i = m_selectedItem-1; while (i>-1 && m_eventList->getHash(i) != hashId) i--; if (i == -1) { i = m_eventList->count()-1; while (i>m_selectedItem && m_eventList->getHash(i) != hashId) i--; } setSelectedItem(i); } else { // select last in view or before int newIndex = m_selectedItem-1; if (newIndex < 0) newIndex = m_eventList->count()-1; if (m_eventList->getStartTime(newIndex) > m_endTime) newIndex = m_eventList->findLastIndex(m_endTime); setSelectedItem(newIndex); } }