]> git.cworth.org Git - apitrace/blobdiff - gui/graphing/graphwidget.cpp
Rewrote profile graph drawing code.
[apitrace] / gui / graphing / graphwidget.cpp
diff --git a/gui/graphing/graphwidget.cpp b/gui/graphing/graphwidget.cpp
new file mode 100644 (file)
index 0000000..88a86cc
--- /dev/null
@@ -0,0 +1,583 @@
+#include "graphwidget.h"
+
+#include <QScrollBar>
+
+GraphWidget::GraphWidget(QWidget* parent) :
+    QWidget(parent),
+    m_view(NULL),
+    m_label(NULL),
+    m_axisTop(NULL),
+    m_axisLeft(NULL),
+    m_axisRight(NULL),
+    m_axisBottom(NULL),
+    m_horizontalScrollbar(NULL),
+    m_horizontalMin(0),
+    m_horizontalMax(0),
+    m_horizontalStart(0),
+    m_horizontalEnd(0),
+    m_horizontalScrollbarPolicy(Qt::ScrollBarAlwaysOff),
+    m_verticalScrollbar(NULL),
+    m_verticalMin(0),
+    m_verticalMax(0),
+    m_verticalStart(0),
+    m_verticalEnd(0),
+    m_verticalScrollbarPolicy(Qt::ScrollBarAlwaysOff)
+{
+    m_selection.type = SelectionState::None;
+
+    m_verticalScrollbar = new QScrollBar(this);
+    m_verticalScrollbar->setOrientation(Qt::Vertical);
+    m_verticalScrollbar->hide();
+    m_verticalScrollbar->resize(m_verticalScrollbar->sizeHint());
+
+    m_horizontalScrollbar = new QScrollBar(this);
+    m_horizontalScrollbar->setOrientation(Qt::Horizontal);
+    m_horizontalScrollbar->hide();
+    m_horizontalScrollbar->resize(m_horizontalScrollbar->sizeHint());
+
+    updateLayout();
+    setAutoFillBackground(true);
+}
+
+
+GraphView* GraphWidget::view()
+{
+    return m_view;
+}
+
+
+GraphLabelWidget* GraphWidget::label()
+{
+    return m_label;
+}
+
+
+GraphAxisWidget* GraphWidget::axis(AxisPosition pos)
+{
+    switch(pos) {
+        case AxisTop:
+            return m_axisTop;
+
+        case AxisLeft:
+            return m_axisLeft;
+
+        case AxisRight:
+            return m_axisRight;
+
+        case AxisBottom:
+            return m_axisBottom;
+
+        default:
+            return NULL;
+    }
+}
+
+
+void GraphWidget::setView(GraphView* view)
+{
+    delete m_view;
+    m_view = view;
+
+    updateConnections();
+
+    m_view->setSelectionState(&m_selection);
+    m_view->show();
+    m_view->update();
+}
+
+
+void GraphWidget::setLabel(GraphLabelWidget* label)
+{
+    delete m_label;
+    m_label = label;
+}
+
+
+void GraphWidget::setAxis(AxisPosition pos, GraphAxisWidget* axis)
+{
+    switch(pos) {
+        case AxisTop:
+            delete m_axisTop;
+            m_axisTop = axis;
+            m_axisTop->setOrientation(GraphAxisWidget::Horizontal);
+            m_axisTop->setSelectionState(&m_selection);
+            break;
+
+        case AxisLeft:
+            delete m_axisLeft;
+            m_axisLeft = axis;
+            m_axisLeft->setOrientation(GraphAxisWidget::Vertical);
+            m_axisLeft->setSelectionState(&m_selection);
+            break;
+
+        case AxisRight:
+            delete m_axisRight;
+            m_axisRight = axis;
+            m_axisRight->setOrientation(GraphAxisWidget::Vertical);
+            m_axisRight->setSelectionState(&m_selection);
+            break;
+
+        case AxisBottom:
+            delete m_axisBottom;
+            m_axisBottom = axis;
+            m_axisBottom->setOrientation(GraphAxisWidget::Horizontal);
+            m_axisBottom->setSelectionState(&m_selection);
+            break;
+    }
+
+    updateConnections();
+    updateSelection();
+    axis->show();
+}
+
+
+void GraphWidget::setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy policy)
+{
+    m_horizontalScrollbarPolicy = policy;
+    updateScrollbars();
+}
+
+
+void GraphWidget::setVerticalScrollBarPolicy(Qt::ScrollBarPolicy policy)
+{
+    m_verticalScrollbarPolicy = policy;
+    updateScrollbars();
+}
+
+
+void GraphWidget::resizeEvent(QResizeEvent *e)
+{
+    updateLayout();
+    update();
+}
+
+
+/* Used if a selection would be shared between graphs with different axis */
+SelectionState GraphWidget::transformSelectionIn(SelectionState state)
+{
+    return state;
+}
+
+
+/* Used if a selection would be shared between graphs with different axis */
+SelectionState GraphWidget::transformSelectionOut(SelectionState state)
+{
+    return state;
+}
+
+
+/* Update the scrollbars based on current view */
+void GraphWidget::updateScrollbars()
+{
+    /* Vertical scroll bar */
+    qint64 size = (m_verticalMax - m_verticalMin) - (m_verticalEnd - m_verticalStart);
+
+    if (size <= INT_MAX) {
+        m_verticalScrollbar->setValue(m_verticalStart - m_verticalMin);
+        m_verticalScrollbar->setPageStep(m_verticalEnd - m_verticalStart);
+        m_verticalScrollbar->setRange(0, size);
+    } else {
+        /* QScrollBar only supports up to INT_MAX values,
+         * here we must scale our values to match this */
+        double curSize = m_verticalEnd - m_verticalStart;
+        double pages = (m_verticalMax - m_verticalMin) / curSize;
+        double value = (m_verticalStart - m_verticalMin) / curSize;
+
+        m_verticalScrollbar->setValue(value);
+        m_verticalScrollbar->setPageStep(1);
+        m_verticalScrollbar->setRange(0, pages);
+    }
+
+    /* Adhere to scrollbar policy */
+    bool visible = false;
+
+    if (m_verticalScrollbarPolicy == Qt::ScrollBarAlwaysOn) {
+        visible = true;
+    } else if (m_verticalScrollbarPolicy == Qt::ScrollBarAlwaysOff) {
+        visible = false;
+    } else if (m_verticalScrollbarPolicy == Qt::ScrollBarAsNeeded) {
+        visible = m_verticalMin != m_verticalStart || m_verticalMax != m_verticalEnd;
+    }
+
+    if (visible != m_verticalScrollbar->isVisible()) {
+        m_verticalScrollbar->setVisible(visible);
+        updateLayout();
+    }
+
+    /* Horizontal scroll bar */
+    size = (m_horizontalMax - m_horizontalMin) - (m_horizontalEnd - m_horizontalStart);
+
+    if (size <= INT_MAX) {
+        m_horizontalScrollbar->setValue(m_horizontalStart - m_horizontalMin);
+        m_horizontalScrollbar->setPageStep(m_horizontalEnd - m_horizontalStart);
+        m_horizontalScrollbar->setRange(0, size);
+    } else {
+        /* QScrollBar only supports up to INT_MAX values,
+         * here we must scale our values to match this */
+        double dxdv = INT_MAX / (double)size;
+        double value = (m_horizontalStart - m_horizontalMin) * dxdv;
+        double pageStep = (m_horizontalEnd - m_horizontalStart) * dxdv;
+
+        m_horizontalScrollbar->setValue((int)value);
+        m_horizontalScrollbar->setPageStep((int)pageStep);
+        m_horizontalScrollbar->setRange(0, INT_MAX);
+    }
+
+    /* Adhere to scrollbar policy */
+    visible = false;
+
+    if (m_horizontalScrollbarPolicy == Qt::ScrollBarAlwaysOn) {
+        visible = true;
+    } else if (m_horizontalScrollbarPolicy == Qt::ScrollBarAlwaysOff) {
+        visible = false;
+    } else if (m_horizontalScrollbarPolicy == Qt::ScrollBarAsNeeded) {
+        visible =  m_horizontalMin != m_horizontalStart || m_horizontalMax != m_horizontalEnd;
+    }
+
+    if (visible != m_horizontalScrollbar->isVisible()) {
+        m_horizontalScrollbar->setVisible(visible);
+        updateLayout();
+    }
+}
+
+
+/* Update all signal / slot connections */
+void GraphWidget::updateConnections()
+{
+    if (m_view) {
+        connect(m_view, SIGNAL(selectionChanged()), this, SLOT(updateSelection()), Qt::UniqueConnection);
+
+        connect(m_view, SIGNAL(horizontalViewChanged(qint64,qint64)), this, SLOT(horizontalViewChange(qint64,qint64)), Qt::UniqueConnection);
+        connect(m_view, SIGNAL(horizontalRangeChanged(qint64,qint64)), this, SLOT(horizontalRangeChange(qint64,qint64)), Qt::UniqueConnection);
+
+        connect(m_view, SIGNAL(verticalViewChanged(qint64,qint64)), this, SLOT(verticalViewChange(qint64,qint64)), Qt::UniqueConnection);
+        connect(m_view, SIGNAL(verticalRangeChanged(qint64,qint64)), this, SLOT(verticalRangeChange(qint64,qint64)), Qt::UniqueConnection);
+    }
+
+    if (m_axisTop) {
+        if (m_view) {
+            connect(m_view, SIGNAL(horizontalViewChanged(qint64,qint64)), m_axisTop, SLOT(setView(qint64,qint64)), Qt::UniqueConnection);
+            connect(m_view, SIGNAL(horizontalRangeChanged(qint64,qint64)), m_axisTop, SLOT(setRange(qint64,qint64)), Qt::UniqueConnection);
+        }
+
+        connect(m_axisTop, SIGNAL(selectionChanged()), this, SLOT(updateSelection()), Qt::UniqueConnection);
+    }
+
+    if (m_axisLeft) {
+        if (m_view) {
+            connect(m_view, SIGNAL(verticalViewChanged(qint64,qint64)), m_axisLeft, SLOT(setView(qint64,qint64)), Qt::UniqueConnection);
+            connect(m_view, SIGNAL(verticalRangeChanged(qint64,qint64)), m_axisLeft, SLOT(setRange(qint64,qint64)), Qt::UniqueConnection);
+        }
+
+        connect(m_axisLeft, SIGNAL(selectionChanged()), this, SLOT(updateSelection()), Qt::UniqueConnection);
+    }
+
+    if (m_axisRight) {
+        if (m_view) {
+            connect(m_view, SIGNAL(verticalViewChanged(qint64,qint64)), m_axisRight, SLOT(setView(qint64,qint64)), Qt::UniqueConnection);
+            connect(m_view, SIGNAL(verticalRangeChanged(qint64,qint64)), m_axisRight, SLOT(setRange(qint64,qint64)), Qt::UniqueConnection);
+        }
+
+        connect(m_axisRight, SIGNAL(selectionChanged()), this, SLOT(updateSelection()), Qt::UniqueConnection);
+    }
+
+    if (m_axisBottom) {
+        if (m_view) {
+            connect(m_view, SIGNAL(horizontalViewChanged(qint64,qint64)), m_axisBottom, SLOT(setView(qint64,qint64)), Qt::UniqueConnection);
+            connect(m_view, SIGNAL(horizontalRangeChanged(qint64,qint64)), m_axisBottom, SLOT(setRange(qint64,qint64)), Qt::UniqueConnection);
+        }
+
+        connect(m_axisBottom, SIGNAL(selectionChanged()), this, SLOT(updateSelection()), Qt::UniqueConnection);
+    }
+
+    if (m_horizontalScrollbar) {
+        connect(m_horizontalScrollbar, SIGNAL(actionTriggered(int)), this, SLOT(horizontalScrollAction(int)));
+    }
+
+    if (m_verticalScrollbar) {
+        connect(m_verticalScrollbar, SIGNAL(actionTriggered(int)), this, SLOT(verticalScrollAction(int)));
+    }
+}
+
+
+/* Recalculate the layout */
+void GraphWidget::updateLayout()
+{
+    int x, y;
+    int padX = 0, padY = 0;
+
+    if (m_axisTop) {
+        padY += m_axisTop->height();
+    }
+
+    if (m_axisBottom) {
+        padY += m_axisBottom->height();
+    }
+
+    if (m_axisLeft) {
+        padX += m_axisLeft->width();
+    }
+
+    if (m_axisRight) {
+        padX += m_axisRight->width();
+    }
+
+    if (m_horizontalScrollbar->isVisible()) {
+        padY += m_horizontalScrollbar->height();
+    }
+
+    if (m_verticalScrollbar->isVisible()) {
+        padX += m_verticalScrollbar->width();
+    }
+
+    if (m_axisTop) {
+        x = m_axisLeft ? m_axisLeft->width() : 0;
+        y = 0;
+
+        m_axisTop->move(x, y);
+        m_axisTop->resize(width() - padX, m_axisTop->height());
+    }
+
+    if (m_axisBottom) {
+        x = m_axisLeft ? m_axisLeft->width() : 0;
+        y = height() - m_axisBottom->height();
+
+        if (m_horizontalScrollbar->isVisible())  {
+            y -= m_horizontalScrollbar->height();
+        }
+
+        m_axisBottom->move(x, y);
+        m_axisBottom->resize(width() - padX, m_axisBottom->height());
+    }
+
+    if (m_axisLeft) {
+        x = 0;
+        y = m_axisTop ? m_axisTop->height() : 0;
+
+        m_axisLeft->move(x, y);
+        m_axisLeft->resize(m_axisLeft->width(), height() - padY);
+    }
+
+    if (m_axisRight) {
+        x = width() - m_axisRight->width();
+        y = m_axisTop ? m_axisTop->height() : 0;
+
+        if (m_verticalScrollbar->isVisible())  {
+            x -= m_verticalScrollbar->width();
+        }
+
+        m_axisRight->move(x, y);
+        m_axisRight->resize(m_axisRight->width(), height() - padY);
+    }
+
+    if (m_view) {
+        x = m_axisLeft ? m_axisLeft->width() : 0;
+        y = m_axisTop ? m_axisTop->height() : 0;
+
+        m_view->move(x, y);
+        m_view->resize(width() - padX, height() - padY);
+    }
+
+    if (m_label) {
+        if (m_axisTop && m_axisLeft) {
+            m_label->move(0, 0);
+            m_label->resize(m_axisLeft->width(), m_axisTop->height());
+        }
+    }
+
+    if (m_verticalScrollbar) {
+        m_verticalScrollbar->move(width() - m_verticalScrollbar->width(), 0);
+
+        if (m_horizontalScrollbar) {
+            m_verticalScrollbar->resize(m_verticalScrollbar->width(), height() - m_horizontalScrollbar->height());
+        } else {
+            m_verticalScrollbar->resize(m_verticalScrollbar->width(), height());
+        }
+    }
+
+    if (m_horizontalScrollbar) {
+        m_horizontalScrollbar->move(0, height() - m_horizontalScrollbar->height());
+
+        if (m_verticalScrollbar) {
+            m_horizontalScrollbar->resize(width() - m_verticalScrollbar->width(), m_horizontalScrollbar->height());
+        } else {
+            m_horizontalScrollbar->resize(width(), m_horizontalScrollbar->height());
+        }
+    }
+}
+
+
+void GraphWidget::setSelection(SelectionState state)
+{
+    m_selection = transformSelectionIn(state);
+    updateSelection(false);
+}
+
+
+void GraphWidget::setHorizontalView(qint64 start, qint64 end)
+{
+    if (m_view) {
+        m_view->setHorizontalView(start, end);
+    }
+}
+
+
+void GraphWidget::setVerticalView(qint64 start, qint64 end)
+{
+    if (m_view) {
+        m_view->setVerticalView(start, end);
+    }
+}
+
+
+/* Called when the view is translated / zoomed */
+void GraphWidget::verticalViewChange(qint64 start, qint64 end)
+{
+    m_verticalStart = start;
+    m_verticalEnd = end;
+    updateScrollbars();
+
+    emit verticalViewChanged(start, end);
+}
+
+
+void GraphWidget::verticalRangeChange(qint64 start, qint64 end)
+{
+    m_verticalMin = start;
+    m_verticalMax = end;
+    updateScrollbars();
+
+    emit verticalRangeChanged(start, end);
+}
+
+
+void GraphWidget::horizontalViewChange(qint64 start, qint64 end)
+{
+    m_horizontalStart = start;
+    m_horizontalEnd = end;
+    updateScrollbars();
+
+    emit horizontalViewChanged(start, end);
+}
+
+
+void GraphWidget::horizontalRangeChange(qint64 start, qint64 end)
+{
+    m_horizontalMin = start;
+    m_horizontalMax = end;
+    updateScrollbars();
+
+    emit horizontalRangeChanged(start, end);
+}
+
+
+/* User interaction with horizontal scroll bar */
+void GraphWidget::horizontalScrollAction(int /*action*/)
+{
+    int value = m_horizontalScrollbar->sliderPosition();
+    qint64 size = (m_horizontalMax - m_horizontalMin) - (m_horizontalEnd - m_horizontalStart);
+
+    /* Calculate the new scroll values */
+    if (size <= INT_MAX) {
+        m_horizontalEnd -= m_horizontalStart;
+        m_horizontalStart = value + m_horizontalMin;
+        m_horizontalEnd += value;
+    } else {
+        /* QScrollBar only supports up to INT_MAX values, here we must scale
+         * our values to match this */
+        double dxdv = INT_MAX / (double)size;
+
+        size = m_horizontalEnd - m_horizontalStart;
+        m_horizontalStart = value / dxdv + m_horizontalMin;
+        m_horizontalEnd = m_horizontalStart + size;
+    }
+
+    /* Update view */
+    if (m_view) {
+        m_view->setHorizontalView(m_horizontalStart, m_horizontalEnd);
+    }
+
+    /* Update horizontal axes */
+    if (m_axisTop) {
+        m_axisTop->setView(m_horizontalStart, m_horizontalEnd);
+    }
+
+    if (m_axisBottom) {
+        m_axisBottom->setView(m_horizontalStart, m_horizontalEnd);
+    }
+
+    /* Inform the world of our changes! */
+    emit horizontalViewChanged(m_horizontalStart, m_horizontalEnd);
+}
+
+
+/* User interaction with vertical scroll bar */
+void GraphWidget::verticalScrollAction(int /*action*/)
+{
+    int value = m_verticalScrollbar->sliderPosition();
+    qint64 size = (m_verticalMax - m_verticalMin) - (m_verticalEnd - m_verticalStart);
+
+    /* Calculate the new scroll values */
+    if (size <= INT_MAX) {
+        m_verticalEnd -= m_verticalStart;
+        m_verticalStart = value + m_verticalMin;
+        m_verticalEnd += value;
+    } else {
+        /* QScrollBar only supports up to INT_MAX values, here we must scale
+         * our values to match this */
+        double dxdv = INT_MAX / (double)size;
+
+        size = m_verticalEnd - m_verticalStart;
+        m_verticalStart = value / dxdv + m_verticalMin;
+        m_verticalEnd = m_verticalStart + size;
+    }
+
+    /* Update view */
+    if (m_view) {
+        m_view->setVerticalView(m_verticalStart, m_verticalEnd);
+    }
+
+    /* Update vertical axes */
+    if (m_axisLeft) {
+        m_axisLeft->setView(m_verticalStart, m_verticalEnd);
+    }
+
+    if (m_axisRight) {
+        m_axisRight->setView(m_verticalStart, m_verticalEnd);
+    }
+
+    /* Inform the world of our changes! */
+    emit verticalViewChanged(m_verticalStart, m_verticalEnd);
+}
+
+
+/* Update child elements when selection changes */
+void GraphWidget::updateSelection(bool emitSignal)
+{
+    if (m_view) {
+        m_view->update();
+    }
+
+    if (m_axisTop) {
+        m_axisTop->update();
+    }
+
+    if (m_axisLeft) {
+        m_axisLeft->update();
+    }
+
+    if (m_axisRight) {
+        m_axisRight->update();
+    }
+
+    if (m_axisBottom) {
+        m_axisBottom->update();
+    }
+
+    if (emitSignal) {
+        emit selectionChanged(transformSelectionOut(m_selection));
+    }
+}
+
+
+#include "graphwidget.moc"