--- /dev/null
+#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"