| #include "heatmapview.h" |
| |
| #include <qmath.h> |
| #include <QToolTip> |
| #include <QPainter> |
| #include <QMouseEvent> |
| |
| HeatmapView::HeatmapView(QWidget* parent) : |
| GraphView(parent), |
| m_data(NULL) |
| { |
| m_rowHeight = 20; |
| setMouseTracking(true); |
| } |
| |
| |
| void HeatmapView::setDataProvider(HeatmapDataProvider* data) |
| { |
| delete m_data; |
| m_data = data; |
| |
| if (m_data) { |
| m_data->setSelectionState(m_selectionState); |
| |
| setDefaultView(m_data->start(), m_data->end()); |
| |
| m_viewWidthMin = 1000; |
| |
| m_graphBottom = 0; |
| m_graphTop = (m_data->headerRows() + m_data->dataRows()) * m_rowHeight; |
| } else { |
| setDefaultView(0, 0); |
| m_graphBottom = m_graphTop = 0; |
| } |
| |
| update(); |
| } |
| |
| |
| void HeatmapView::setSelectionState(SelectionState* state) |
| { |
| if (m_data) { |
| m_data->setSelectionState(state); |
| } |
| |
| GraphView::setSelectionState(state); |
| } |
| |
| |
| void HeatmapView::mouseMoveEvent(QMouseEvent *e) |
| { |
| GraphView::mouseMoveEvent(e); |
| |
| if (e->buttons() || !m_data) { |
| QToolTip::hideText(); |
| return; |
| } |
| |
| qint64 index = itemAtPosition(e->pos()); |
| |
| if (index >= 0) { |
| QToolTip::showText(e->globalPos(), m_data->itemTooltip(index)); |
| } else { |
| QToolTip::hideText(); |
| } |
| } |
| |
| |
| void HeatmapView::mouseDoubleClickEvent(QMouseEvent *e) |
| { |
| if (m_data && e->button() == Qt::LeftButton) { |
| qint64 index = itemAtPosition(e->pos()); |
| |
| if (index >= 0) { |
| m_data->itemDoubleClicked(index); |
| return; |
| } |
| } |
| |
| GraphView::mouseDoubleClickEvent(e); |
| } |
| |
| |
| void HeatmapView::paintEvent(QPaintEvent *) |
| { |
| if (!m_data) { |
| return; |
| } |
| |
| QPainter painter(this); |
| painter.fillRect(0, m_data->headerRows() * m_rowHeight, width(), height(), Qt::white); |
| |
| /* Draw data rows */ |
| painter.translate(0, m_data->headerRows() * m_rowHeight - m_viewBottom % m_rowHeight); |
| int rowStart = m_viewBottom / m_rowHeight; |
| int rowEnd = qMin<int>(qCeil(m_viewTop / (double)m_rowHeight), m_data->dataRows()); |
| |
| for (unsigned i = rowStart; i < rowEnd; ++i) { |
| HeatmapRowIterator* itr = m_data->dataRowIterator(i, m_viewLeft, m_viewRight, width()); |
| paintRow(painter, itr); |
| painter.translate(0, m_rowHeight); |
| delete itr; |
| } |
| |
| /* Draw Header */ |
| painter.resetTransform(); |
| painter.fillRect(0, 0, width(), m_data->headerRows() * m_rowHeight, Qt::white); |
| |
| for (unsigned i = 0; i < m_data->headerRows(); ++i) { |
| HeatmapRowIterator* itr = m_data->headerRowIterator(i, m_viewLeft, m_viewRight, width()); |
| paintRow(painter, itr); |
| painter.translate(0, m_rowHeight); |
| delete itr; |
| } |
| |
| /* Draw Axis Lines */ |
| painter.resetTransform(); |
| painter.setPen(Qt::black); |
| painter.drawLine(0, m_rowHeight, width(), m_rowHeight); |
| painter.drawLine(0, m_rowHeight * 2, width(), m_rowHeight * 2); |
| |
| painter.setPen(QColor(240, 240, 240)); |
| painter.translate(0, m_data->headerRows() * m_rowHeight - m_viewBottom % m_rowHeight); |
| |
| for (unsigned i = rowStart; i < rowEnd; ++i) { |
| painter.drawLine(0, m_rowHeight, width(), m_rowHeight); |
| painter.translate(0, m_rowHeight); |
| } |
| |
| /* Draw selection borders */ |
| painter.resetTransform(); |
| painter.setPen(Qt::green); |
| |
| if (m_selectionState->type == SelectionState::Horizontal) { |
| double dxdt = width() / (double)m_viewWidth; |
| double scroll = m_viewLeft * dxdt; |
| double left = (m_selectionState->start * dxdt) - scroll; |
| double right = (m_selectionState->end * dxdt) - scroll; |
| |
| /* Highlight time */ |
| if (left >= 0 && left <= width()) { |
| painter.drawLine(left, 0, left, height()); |
| } |
| |
| if (right >= 0 && right <= width()) { |
| painter.drawLine(right, 0, right, height()); |
| } |
| } else if (m_selectionState->type == SelectionState::Vertical) { |
| /* Highlight row */ |
| int row = m_selectionState->start; |
| |
| for (unsigned i = rowStart; i < rowEnd; ++i) { |
| if (m_data->dataRowAt(i) == row) { |
| row = i - rowStart; |
| |
| painter.translate(0, m_data->headerRows() * m_rowHeight - m_viewBottom % m_rowHeight); |
| painter.drawLine(0, (row + 1) * m_rowHeight, width(), (row + 1) * m_rowHeight); |
| |
| if (row > 0) { |
| painter.drawLine(0, row * m_rowHeight, width(), row * m_rowHeight); |
| } |
| |
| break; |
| } |
| } |
| } |
| } |
| |
| |
| void HeatmapView::paintRow(QPainter& painter, HeatmapRowIterator* itr) |
| { |
| bool selection = m_selectionState ? m_selectionState->type != SelectionState::None : false; |
| |
| while (itr->next()) |
| { |
| double heat = itr->heat(); |
| int width = itr->width(); |
| int x = itr->step(); |
| |
| /* Gamma correction */ |
| heat = qPow(heat, 1.0 / 2.0); |
| |
| if (width == 1) { |
| /* Draw single line */ |
| if (selection) { |
| double selectedHeat = itr->selectedHeat(); |
| |
| if (selectedHeat >= 0.999) { |
| heat = 255.0 - qBound(0.0, heat * 255.0, 255.0); |
| |
| if (itr->isGpu()) { |
| painter.setPen(QColor(255, heat, heat)); |
| } else { |
| painter.setPen(QColor(heat, heat, 255)); |
| } |
| |
| painter.drawLine(x, 0, x, m_rowHeight - 1); |
| } else { |
| heat = 255.0 - qBound(0.0, heat * 100.0, 100.0); |
| painter.setPen(QColor(heat, heat, heat)); |
| painter.drawLine(x, 0, x, m_rowHeight - 1); |
| |
| if (selectedHeat > 0.001) { |
| selectedHeat = qPow(selectedHeat, 1.0 / 2.0); |
| selectedHeat = qBound(0.0, selectedHeat * 255.0, 255.0); |
| |
| if (itr->isGpu()) { |
| painter.setPen(QColor(255, 0, 0, selectedHeat)); |
| } else { |
| painter.setPen(QColor(0, 0, 255, selectedHeat)); |
| } |
| |
| painter.drawLine(x, 0, x, m_rowHeight - 1); |
| } |
| } |
| } else { |
| heat = qBound(0.0, heat * 255.0, 255.0); |
| |
| if (itr->isGpu()) { |
| painter.setPen(QColor(255, 255 - heat, 255 - heat)); |
| } else { |
| painter.setPen(QColor(255 - heat, 255 - heat, 255)); |
| } |
| |
| painter.drawLine(x, 0, x, m_rowHeight - 1); |
| } |
| } else { |
| double selectedHeat = itr->selectedHeat(); |
| |
| if (selection && selectedHeat < 0.9) { |
| painter.fillRect(x, 0, width, m_rowHeight, QColor(255 - 100, 255 - 100, 255 - 100)); |
| } else if (itr->isGpu()) { |
| painter.fillRect(x, 0, width, m_rowHeight, QColor(255, 0, 0)); |
| } else { |
| painter.fillRect(x, 0, width, m_rowHeight, QColor(0, 0, 255)); |
| } |
| |
| if (width > 6) { |
| painter.setPen(Qt::white); |
| QString elided = painter.fontMetrics().elidedText(itr->label(), Qt::ElideRight, width - 1); |
| |
| painter.drawText(x + 1, 0, width - 1, m_rowHeight - 1, |
| Qt::AlignLeft | Qt::AlignVCenter, |
| elided); |
| } |
| } |
| } |
| } |
| |
| |
| qint64 HeatmapView::itemAtPosition(QPoint pos) |
| { |
| if (!m_data) { |
| return -1; |
| } |
| |
| double t = m_viewWidth / (double)width(); |
| t *= pos.x(); |
| t += m_viewLeft; |
| |
| qint64 time = (qint64)t; |
| qint64 index; |
| |
| if (pos.y() < m_data->headerRows() * m_rowHeight) { |
| int row = pos.y() / m_rowHeight; |
| index = m_data->headerItemAt(row, time); |
| } else { |
| int row = pos.y(); |
| row -= m_data->headerRows() * m_rowHeight; |
| row += m_viewBottom; |
| row /= m_rowHeight; |
| index = m_data->dataItemAt(row, time); |
| } |
| |
| return index; |
| } |