1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-04-26 10:19:45 +03:00
2012-03-05 19:23:40 +04:00

3018 lines
97 KiB
C++
Executable File

///////////////////////////////////////////////////////////////////////
// File : Graph.cpp
// Created : 26.03.02
// Author : Vadim SANDLER
//////////////////////////////////////////////////////////////////////
#include <Graph.h>
#include <math.h>
#include <float.h>
#include <qpainter.h>
#include <qapplication.h>
#include <qbitmap.h>
#include <qpushbutton.h>
#include <qlayout.h>
#include <qpolygon.h>
#include <qevent.h>
#include <qtooltip.h>
static const char* const zoomCursorImage[] = {
"32 32 3 1",
". c None",
"a c #000000",
"# c #ffffff",
"................................",
"................................",
".#######........................",
"..aaaaaaa.......................",
"................................",
".............#####..............",
"...........##.aaaa##............",
"..........#.aa.....a#...........",
".........#.a.........#..........",
".........#a..........#a.........",
"........#.a...........#.........",
"........#a............#a........",
"........#a............#a........",
"........#a............#a........",
"........#a............#a........",
".........#...........#.a........",
".........#a..........#a.........",
".........##.........#.a.........",
"........#####.....##.a..........",
".......###aaa#####.aa...........",
"......###aa...aaaaa.......#.....",
".....###aa................#a....",
"....###aa.................#a....",
"...###aa...............#######..",
"....#aa.................aa#aaaa.",
".....a....................#a....",
"..........................#a....",
"...........................a....",
"................................",
"................................",
"................................",
"................................"};
static const char* const zoomXCursorImage[] = {
"32 32 3 1",
". c None",
"a c #000000",
"# c #ffffff",
"................................",
"................................",
"................................",
"................................",
"................................",
".............#####..............",
"...........##.aaaa##............",
"..........#.aa.....a#...........",
"....a....#.a.........#.....a....",
"...aa....#a..........#a....aa...",
"..a#a...#.a...........#....a#a..",
".a##aa..#a............#a..aa##a.",
"a#####..#a............#a..#####a",
".a##aa..#a............#a..aa##a.",
"..a#a...#a............#a...a#a..",
"...aa....#...........#.a...aa...",
"....a....#a..........#a....a....",
".........##.........#.a.........",
"........#####.....##.a..........",
".......###aaa#####.aa...........",
"......###aa...aaaaa.............",
".....###aa......................",
"....###aa.......................",
"...###aa........................",
"....#aa.........................",
".....a..........................",
"................................",
"................................",
"................................",
"................................",
"................................",
"................................"};
static const char* const zoomYCursorImage[] = {
"32 32 3 1",
". c None",
"a c #000000",
"# c #ffffff",
"................a...............",
"...............a#a..............",
"..............a###a.............",
".............a#####a............",
"............aaaa#aaaa...........",
"...............a#a..............",
"................................",
"................................",
".............#####..............",
"...........##.aaaa##............",
"..........#.aa.....a#...........",
".........#.a.........#..........",
".........#a..........#a.........",
"........#.a...........#.........",
"........#a............#a........",
"........#a............#a........",
"........#a............#a........",
"........#a............#a........",
".........#...........#.a........",
".........#a..........#a.........",
".........##.........#.a.........",
"........#####.....##.a..........",
".......###aaa#####.aa...........",
"......###aa...aaaaa.............",
".....###aa......................",
"....###aa.......................",
"...###aa.......a#a..............",
"....#aa.....aaaa#aaaa...........",
".....a.......a#####a............",
"..............a###a.............",
"...............a#a..............",
"................a..............."};
#define SPACING_SIZE 5 // spacing
#define TICK_SIZE 3 // axes tick size
#define ARROW_LENGTH 20 // axis arrow size
#define ARROW_WIDTH 3 // ...
#define MIN_GRID_STEP 20 // minimum grid size in pixels
#define MIN_SENSIBILITY_SIZE 10 // minimum sensibility size
#define MIN(x, y) ( (x) < (y) ? (x) : (y) )
#define MAX(x, y) ( (x) > (y) ? (x) : (y) )
//////////////////////////////////////////////////////////////////////
// GraphView
//////////////////////////////////////////////////////////////////////
//================================================================
// Function : GraphView::GraphView
// Purpose : Constructor
//================================================================
GraphView::GraphView(QWidget* parent)
: QWidget(parent), // WResizeNoErase | WRepaintNoErase
myRubberBand(0)
{
// reseting legend
myLegend = 0;
// setting default cursors
setOperationCursor(voNone, Qt::ArrowCursor);
setOperationCursor(voZoom, QCursor(QPixmap((const char**)zoomCursorImage)));
setOperationCursor(voZoomX, QCursor(QPixmap((const char**)zoomXCursorImage)));
setOperationCursor(voZoomY, QCursor(QPixmap((const char**)zoomYCursorImage)));
setOperationCursor(voPan, Qt::SizeAllCursor);
setOperationCursor(voPanX, Qt::SizeHorCursor);
setOperationCursor(voPanY, Qt::SizeVerCursor);
setOperationCursor(voRect, Qt::PointingHandCursor);
// setting default mouse-keyboard hot combinations
setOperationButton(voZoom, Qt::LeftButton, Qt::ControlModifier);
setOperationButton(voZoomX, Qt::LeftButton, Qt::AltModifier);
setOperationButton(voZoomY, Qt::RightButton, Qt::AltModifier);
setOperationButton(voPan, Qt::RightButton, Qt::ControlModifier);
setOperationButton(voPanX, Qt::LeftButton , Qt::ShiftModifier);
setOperationButton(voPanY, Qt::RightButton, Qt::ShiftModifier);
setOperationButton(voRect, Qt::LeftButton, Qt::ControlModifier | Qt::ShiftModifier);
// enabling operations globally
setOperationsEnabled(true);
// disable view operations by default
setOperationEnabled(voZoom, false);
setOperationEnabled(voZoomX, false);
setOperationEnabled(voZoomY, false);
setOperationEnabled(voPan, false);
setOperationEnabled(voPanX, false);
setOperationEnabled(voPanY, false);
setOperationEnabled(voRect, false);
// creating colors
myColors.append (Qt::white);
myColors.append (Qt::blue);
myColors.append (Qt::gray);
myColors.append (Qt::darkGreen);
myColors.append (Qt::magenta);
myColors.append (Qt::darkGray);
myColors.append (Qt::red);
myColors.append (Qt::darkBlue);
myColors.append (Qt::darkYellow);
myColors.append (Qt::cyan);
myColors.append (Qt::darkRed);
myColors.append (Qt::darkCyan);
myColors.append (Qt::yellow);
myColors.append (Qt::darkMagenta);
myColors.append (Qt::green);
myColors.append (Qt::black);
// creating line styles
myLines.append (Qt::SolidLine);
myLines.append (Qt::DashLine);
myLines.append (Qt::DotLine);
myLines.append (Qt::DashDotLine);
myLines.append (Qt::DashDotDotLine);
// initializing graph view
myOperation = myForcedOp = voNone; // clearing current operation
myOrigin = GraphNode(0.0, 0.0); // setting origin to (0,0)
setScale(1.0); // setting scale to 1.0
myShowGrid = true; // enabling grid drawing
myShowAxes = true; // enabling axes drawing
myShowTitle = true; // enabling title drawing
myShowAxesTitle = true; // enabling axes titles drawing
myShowTickMarks = true; // enabling tick marks drawing
myShowTooltips = true; // enabling tooltips
myShowMarkers = true; // enabling markers drawing
setGridStep (100.0, 100.0); // setting default floating grid step
setFixedGridStep(100, 100); // setting default fixed grid step
setGridIntervals(10, 10); // setting default grid intervals
setGridMode(gmFixed); // default grid mode is FIXED
myMarkerSize = 9; // default marker size
setSensibilitySize(MIN_SENSIBILITY_SIZE); // default sensibility size
// starting watching mouse events
setMouseTracking (true);
// setting background color
changeBackgroundColor(0);
}
//================================================================
// Function : GraphView::~GraphView
// Purpose : Destructor
//================================================================
GraphView::~GraphView() {
qDeleteAll( myItems );
}
//================================================================
// Function : GraphView::eventFilter
// Purpose : event filter
//================================================================
bool GraphView::eventFilter(QObject* object, QEvent* event) {
if ( ( event->type() == QEvent::MouseButtonPress ||
event->type() == QEvent::KeyPress ) &&
object != this) {
startOperation(voNone);
qApp->removeEventFilter(this);
}
return QWidget::eventFilter(object, event);
}
//================================================================
// Function : GraphView::setOperationCursor
// Purpose : sets cursor for certain view operation
//================================================================
void GraphView::setOperationCursor (const ViewOperation operation,
const QCursor& newCursor) {
switch(operation) {
case voZoom:
myZoomCursor = newCursor;
break;
case voZoomX:
myZoomXCursor = newCursor;
break;
case voZoomY:
myZoomYCursor = newCursor;
break;
case voPan:
myPanCursor = newCursor;
break;
case voPanX:
myPanXCursor = newCursor;
break;
case voPanY:
myPanYCursor = newCursor;
break;
case voRect:
myRectCursor = newCursor;
break;
case voNone:
default:
myDefCursor = newCursor;
break;
}
}
//================================================================
// Function : GraphView::getOperationCursor
// Purpose : gets cursor for certain view operation
//================================================================
QCursor GraphView::getOperationCursor (const ViewOperation operation) const {
switch(operation) {
case voZoom:
return myZoomCursor;
case voZoomX:
return myZoomXCursor;
case voZoomY:
return myZoomYCursor;
case voPan:
return myPanCursor;
case voPanX:
return myPanXCursor;
case voPanY:
return myPanYCursor;
case voRect:
return myRectCursor;
case voNone:
default:
break;
}
return myDefCursor;
}
//================================================================
// Function : GraphView::setOperationsEnabled
// Purpose : globally enable/disables operations
//================================================================
void GraphView::setOperationsEnabled(bool on) {
myEnableGlobal = on;
}
//================================================================
// Function : GraphView::setOperationEnabled
// Purpose : enables/disables view operation
//================================================================
void GraphView::setOperationEnabled(const ViewOperation operation,
bool enable) {
switch(operation) {
case voZoom:
myEnableZoom = enable;
break;
case voZoomX:
myEnableZoomX = enable;
break;
case voZoomY:
myEnableZoomY = enable;
break;
case voPan:
myEnablePan = enable;
break;
case voPanX:
myEnablePanX = enable;
break;
case voPanY:
myEnablePanY = enable;
break;
case voRect:
myEnableRect = enable;
break;
}
}
//================================================================
// Function : GraphView::isOperationEnabled
// Purpose : returns 'true' if view operation is enabled
//================================================================
bool GraphView::isOperationEnabled(const ViewOperation operation) const {
switch(operation) {
case voZoom:
return myEnableZoom;
case voZoomX:
return myEnableZoomX;
case voZoomY:
return myEnableZoomY;
case voPan:
return myEnablePan;
case voPanX:
return myEnablePanX;
case voPanY:
return myEnablePanY;
case voRect:
return myEnableRect;
}
return false;
}
//================================================================
// Function : GraphView::setOperationButton
// Purpose : sets view operation key-mousebutton combination
//================================================================
void GraphView::setOperationButton(const ViewOperation operation,
const Qt::MouseButtons button,
const Qt::KeyboardModifiers modifier) {
switch (operation) {
case voZoom:
myZoomKey = OperationButton(button, modifier);
break;
case voZoomX:
myZoomXKey = OperationButton(button, modifier);
break;
case voZoomY:
myZoomYKey = OperationButton(button, modifier);
break;
case voPan:
myPanKey = OperationButton(button, modifier);
break;
case voPanX:
myPanXKey = OperationButton(button, modifier);
break;
case voPanY:
myPanYKey = OperationButton(button, modifier);
break;
case voRect:
myRectKey = OperationButton(button, modifier);
break;
}
}
//================================================================
// Function : GraphView::getOperationButton
// Purpose : gets operation key-mousebutton combination
//================================================================
void GraphView::getOperationButton(const ViewOperation operation,
Qt::MouseButtons& button,
Qt::KeyboardModifiers& modifier) const {
switch (operation) {
case voZoom:
button = myZoomKey.button;
modifier = myZoomKey.modifier;
break;
case voZoomX:
button = myZoomXKey.button;
modifier = myZoomXKey.modifier;
break;
case voZoomY:
button = myZoomYKey.button;
modifier = myZoomYKey.modifier;
break;
case voPan:
button = myPanKey.button;
modifier = myPanKey.modifier;
break;
case voPanX:
button = myPanXKey.button;
modifier = myPanXKey.modifier;
break;
case voPanY:
button = myPanYKey.button;
modifier = myPanYKey.modifier;
break;
case voRect:
button = myRectKey.button;
modifier = myRectKey.modifier;
break;
case voNone:
default:
button = Qt::NoButton;
modifier = Qt::NoModifier;
break;
}
}
//================================================================
// Function : GraphView::testOperation
// Purpose : tests for operation key-mousebutton combination
//================================================================
bool GraphView::testOperation (const ViewOperation operation,
const Qt::MouseButtons button,
const Qt::KeyboardModifiers modifier) const {
switch (operation) {
case voZoom:
return myZoomKey.button == button && myZoomKey.modifier == modifier;
case voZoomX:
return myZoomXKey.button == button && myZoomXKey.modifier == modifier;
case voZoomY:
return myZoomYKey.button == button && myZoomYKey.modifier == modifier;
case voPan:
return myPanKey.button == button && myPanKey.modifier == modifier;
case voPanX:
return myPanXKey.button == button && myPanXKey.modifier == modifier;
case voPanY:
return myPanYKey.button == button && myPanYKey.modifier == modifier;
case voRect:
return myRectKey.button == button && myRectKey.modifier == modifier;
default:
break;
}
return false;
}
//================================================================
// Function : GraphView::testOperation
// Purpose : tests key-mousebutton combination and
// returns view operation if any matches
//================================================================
GraphView::ViewOperation GraphView::testOperation(const Qt::MouseButtons button,
const Qt::KeyboardModifiers modifier) const {
if (testOperation(voZoom, button, modifier))
return voZoom;
if (testOperation(voZoomX, button, modifier))
return voZoomX;
if (testOperation(voZoomY, button, modifier))
return voZoomY;
if (testOperation(voPan, button, modifier))
return voPan;
if (testOperation(voPanX, button, modifier))
return voPanX;
if (testOperation(voPanY, button, modifier))
return voPanY;
if (testOperation(voRect, button, modifier))
return voRect;
return voNone;
}
//================================================================
// Function : GraphView::reset
// Purpose : resets view - sets origin to 0.0 and scales to 1.0
//================================================================
void GraphView::reset() {
myOrigin = GraphNode(0.0, 0.0);
myXScale = 1.0;
myYScale = 1.0;
update(); //repaint(false);
}
//================================================================
// Function : GraphView::fitAll
// Purpose : fits viewer so display all data
//================================================================
void GraphView::fitAll() {
QRect pRect = getGraphRect();
if (!pRect.isValid() ||
pRect.right() == pRect.left() ||
pRect.top() == pRect.bottom())
return;
GraphItem* item;
double xMin = DBL_MAX;
double xMax = -DBL_MAX;
double yMin = DBL_MAX;
double yMax = -DBL_MAX;
int cnt = 0;
foreach (item, myItems ) {
int i;
cnt += item->count();
for (i = 0; i < item->count(); i++) {
GraphNode* node = item->getNode(i);
if (node->x() < xMin)
xMin = node->x();
if (node->x() > xMax)
xMax = node->x();
if (node->y() < yMin)
yMin = node->y();
if (node->y() > yMax)
yMax = node->y();
}
}
if (cnt > 0) {
double xOr = xMin;
double yOr = yMin;
double dx = xMax - xMin;
double dy = yMax - yMin;
int idx = pRect.width();
int idy = pRect.height();
if (dx > 0) {
myXScale = dx * 1.001 / idx;
}
else {
myXScale = 10 * fabs(xMin) / idx;
xOr = xMin - 5 * fabs(xMin);
}
if (dy > 0) {
myYScale = dy * 1.001 / idy;
}
else {
myYScale = 10 * fabs(yMin) / idy;
yOr = yMin - 5 * fabs(yMin);
}
myOrigin = GraphNode(xOr, yOr);
update(); //repaint(false);
}
}
//================================================================
// Function : GraphView::fitData
// Purpose : fits viewer to display certain data
//================================================================
void GraphView::fitData(const double xMin, const double xMax,
const double yMin, const double yMax) {
QRect pRect = getGraphRect();
if (xMin == xMax || yMin == yMax)
return;
double xOr = (xMin < xMax) ? xMin : xMax;
double yOr = (yMin < yMax) ? yMin : yMax;
double dx = fabs(xMax - xMin);
double dy = fabs(yMax - yMin);
int idx = pRect.width();
int idy = pRect.height();
myXScale = dx * 1.001 / idx;
myYScale = dy * 1.001 / idy;
myOrigin = GraphNode(xOr, yOr);
update(); //repaint(false);
}
//================================================================
// Function : GraphView::fitDataX
// Purpose : fits viewer to display certain data along horizontal axis
//================================================================
void GraphView::fitDataX(const double xMin, const double xMax) {
QRect pRect = getGraphRect();
if (xMin == xMax)
return;
double xOr = (xMin < xMax) ? xMin : xMax;
double dx = fabs(xMax - xMin);
int idx = pRect.width();
myXScale = dx * 1.001 / idx;
myOrigin = GraphNode(xOr, myOrigin.y());
update(); //repaint(false);
}
//================================================================
// Function : GraphView::fitDataY
// Purpose : fits viewer to display certain data along vertical axis
//================================================================
void GraphView::fitDataY(const double yMin, const double yMax) {
QRect pRect = getGraphRect();
if (yMin == yMax)
return;
double yOr = (yMin < yMax) ? yMin : yMax;
double dy = fabs(yMax - yMin);
int idy = pRect.height();
myYScale = dy * 1.001 / idy;
myOrigin = GraphNode(myOrigin.x(), yOr);
update(); //repaint(false);
}
//================================================================
// Function : GraphView::pan
// Purpose : performs pan operation by dx, dy pixels
//================================================================
void GraphView::pan(const int dx, const int dy) {
myOrigin = GraphNode(myOrigin.x() - dx * myXScale,
myOrigin.y() + dy * myYScale);
update(); //repaint(false);
}
//================================================================
// Function : GraphView::zoom
// Purpose : performs zoom operation by dx, dy pixels
//================================================================
void GraphView::zoom(const int dx, const int dy) {
double dl = sqrt((double)dx * dx + (double)dy * dy);
bool sn = (abs(dx) > abs(dy)) ? (dx > 0) : (dy > 0);
double pw = pow( 2.0, (dl / 100.0 * ( sn ? (1.0) : (-1.0)) ) );
if (dx != 0 && dy != 0) {
myXScale /= pw;
myYScale /= pw;
}
else
if (dy == 0)
myXScale /= pow( 2.0, ((double)dx)/100 );
else
if (dx == 0)
myYScale /= pow( 2.0, ((double)dy)/100 );
update(); //repaint(false);
}
//================================================================
// Function : GraphView::startViewOperation
// Purpose : starts operation if it is enabled
//================================================================
void GraphView::startViewOperation(const ViewOperation operation) {
if (myOperation != voNone) {
onFinishOperation();
startOperation(voNone);
}
if (isOperationsEnabled() && isOperationEnabled(operation)) {
setCursor(getOperationCursor(operation));
myForcedOp = operation;
qApp->installEventFilter(this);
}
}
//================================================================
// Function : GraphView::showMarkers
// Purpose : enables/disables internal markers drawing
//================================================================
void GraphView::showMarkers(bool show) {
myShowMarkers = show;
update(); //repaint(false);
}
//================================================================
// Function : GraphView::showTooltips
// Purpose : enables/disables tooltips
//================================================================
void GraphView::showTooltips(bool show) {
myShowTooltips = show;
}
//================================================================
// Function : GraphView::showAxes
// Purpose : shows/hides axes
//================================================================
void GraphView::showAxes(bool show) {
if (myShowAxes != show) {
myShowAxes = show;
update(); //repaint(false);
}
}
//================================================================
// Function : GraphView::showTickMark
// Purpose : shows/hides tick-marks
//================================================================
void GraphView::showTickMark(bool show) {
if (myShowTickMarks != show) {
myShowTickMarks = show;
update(); //repaint(false);
}
}
//================================================================
// Function : GraphView::showTitle
// Purpose : shows/hides title
//================================================================
void GraphView::showTitle (bool show) {
if (myShowTitle != show) {
myShowTitle = show;
update(); //repaint(false);
}
}
//================================================================
// Function : GraphView::setTitle
// Purpose : sets graph title
//================================================================
void GraphView::setTitle(const QString& title) {
myTitle = title;
update(); //repaint(false);
}
//================================================================
// Function : GraphView::showAxesTitle
// Purpose : shows/hides axes title
//================================================================
void GraphView::showAxesTitle (bool show) {
if (myShowAxesTitle != show) {
myShowAxesTitle = show;
update(); //repaint(false);
}
}
//================================================================
// Function : GraphView::setXTitle
// Purpose : sets graph X axis title
//================================================================
void GraphView::setXTitle(const QString& title) {
myXTitle = title;
update(); //repaint(false);
}
//================================================================
// Function : GraphView::setYTitle
// Purpose : sets graph Y axis title
//================================================================
void GraphView::setYTitle(const QString& title) {
myYTitle = title;
update(); //repaint(false);
}
//================================================================
// Function : GraphView::showGrid
// Purpose : shows/hides grid
//================================================================
void GraphView::showGrid(bool show) {
if (myShowGrid != show) {
myShowGrid = show;
update(); //repaint(false);
}
}
//================================================================
// Function : GraphView::setGridStep
// Purpose : sets grid step by x, y axes,
// doesn't change step if it is <= 0
//================================================================
void GraphView::setGridStep(const double x, const double y, bool turn) {
if (x > 0) {
myGridStepX = x;
}
if (y > 0) {
myGridStepY = y;
}
if (turn)
myGridMode = gmFloating;
if (myShowGrid)
update(); //repaint(false);
}
//================================================================
// Function : GraphView::getGridStep
// Purpose : gets grid step for x, y axes and returns true if grid is not fixed
//================================================================
bool GraphView::getGridStep(double& x, double& y) const {
x = myGridStepX;
y = myGridStepY;
return (myGridMode == gmFloating);
}
//================================================================
// Function : GraphView::setFixedGridStep
// Purpose : sets fixed grid step for x, y axes in pixels,
// doesn't change step if it is less 0
//================================================================
void GraphView::setFixedGridStep(const int x, const int y, bool turn) {
if (x > 0) {
myGridPStepX = x;
if (myGridPStepX < MIN_GRID_STEP)
myGridPStepX = MIN_GRID_STEP;
}
if (y > 0) {
myGridPStepY = y;
if (myGridPStepY < MIN_GRID_STEP)
myGridPStepY = MIN_GRID_STEP;
}
if (turn)
myGridMode = gmFixed;
if (myShowGrid)
update(); //repaint(false);
}
//================================================================
// Function : GraphView::getFixedGridStep
// Purpose : gets fixed grid step for x, y axes in pixels
// and returns true if grid is fixed
//================================================================
bool GraphView::getFixedGridStep(int& x, int& y) const {
x = myGridPStepX;
y = myGridPStepY;
return (myGridMode == gmFixed);
}
//================================================================
// Function : GraphView::setGridIntervals
// Purpose : sets number of grid intervals (INTERVALS mode) for x, y axes,
// doesn't change if <= 0
//================================================================
void GraphView::setGridIntervals(const int xInt,
const int yInt,
bool turn) {
if (xInt > 0) {
myGridIntX = xInt;
}
if (yInt > 0) {
myGridIntY = yInt;
}
if (turn)
myGridMode = gmIntervals;
if (myShowGrid)
update(); //repaint(false);
}
//================================================================
// Function : GraphView::getGridIntervals
// Purpose : gets number of grid intervals (INTERVALS mode) for x, y axes,
// and returns true if grid mode is INTERVALS
//================================================================
bool GraphView::getGridIntervals(int &xInt, int &yInt) const {
xInt = myGridIntX;
yInt = myGridIntY;
return (myGridMode == gmIntervals);
}
//================================================================
// Function : GraphView::setGridMode
// Purpose : switches grid mode [FIXED, INTERVALS, FLOATING]
//================================================================
void GraphView::setGridMode(const GridMode mode) {
if (myGridMode != mode) {
myGridMode = mode;
if (myShowGrid)
update(); //repaint(false);
}
}
//================================================================
// Function : GraphView::setXScale
// Purpose : sets X axis scale
//================================================================
void GraphView::setXScale(const double scale) {
if (scale > 0) {
myXScale = scale;
update(); //repaint(false);
}
}
//================================================================
// Function : GraphView::setYScale
// Purpose : sets Y axis scale
//================================================================
void GraphView::setYScale(const double scale) {
if (scale > 0) {
myYScale = scale;
update(); //repaint(false);
}
}
//================================================================
// Function : GraphView::setScale
// Purpose : sets X, Y axis scale
//================================================================
void GraphView::setScale(const double scale) {
if (scale > 0) {
myXScale = scale;
myYScale = scale;
update(); //repaint(false);
}
}
//================================================================
// Function : GraphView::setOrigin
// Purpose : moves axes origin to point [x, y]
//================================================================
void GraphView::setOrigin(const double x, const double y) {
myOrigin = GraphNode(x, y);
update(); //repaint(false);
}
//================================================================
// Function : GraphView::getOrigin
// Purpose : gets axes origin
//================================================================
void GraphView::getOrigin(double& x, double& y) const {
x = myOrigin.x();
y = myOrigin.y();
}
//================================================================
// Function : GraphView::changeBackgroundColor
// Purpose :
//================================================================
void GraphView::changeBackgroundColor(const int index) {
int shift = 1;
if (index >= 0 && index < getNbColors())
shift = index;
int i;
unsigned j;
unsigned aNb = myColors.count();
if (aNb > 1) {
for (i = 0; i < shift; i++) {
QColor tmp = myColors.first();
for (j = 0; j < aNb - 1; j++) {
myColors[j] = myColors[j+1];
}
myColors[aNb - 1] = tmp;
}
}
QPalette pal = palette();
pal.setColor(QPalette::Window, myColors.first()); // QPalette::Active,
pal.setColor(QPalette::Text, myColors.last()); //QPalette::Active,
setPalette(pal);
update(); //repaint(false);
if (myLegend)
myLegend->updateMe();
}
//================================================================
// Function : GraphView::setTitleFont
// Purpose : sets main title font
//================================================================
void GraphView::setTitleFont(QFont& font) {
myTitleFont = font;
update(); //repaint(false);
}
//================================================================
// Function : GraphView::setAxesTitleFont
// Purpose : sets axes title font
//================================================================
void GraphView::setAxesTitleFont(QFont& font) {
myAxesTitleFont = font;
update(); //repaint(false);
}
//================================================================
// Function : GraphView::setTickMarksFont
// Purpose : sets tick marks font
//================================================================
void GraphView::setTickMarksFont(QFont& font) {
myTickMarksFont = font;
update(); //repaint(false);
}
//================================================================
// Function : GraphView::setLegend
// Purpose : sets legend widget
//================================================================
void GraphView::setLegend(GraphLegend* legend) {
myLegend = legend;
if (myLegend)
myLegend->updateMe();
}
//================================================================
// Function : GraphView::getBorders
// Purpose : gets borders size
//================================================================
void GraphView::getMargins(int& leftMargin,
int& rightMargin,
int& topMargin,
int& bottomMargin) {
int db = MAX(SPACING_SIZE, (getMarkerSize() + 1)/2);
topMargin = db;
bottomMargin = db;
leftMargin = db;
rightMargin = db;
if (myShowTitle && !myTitle.isEmpty()) {
QFontMetrics fm(myTitleFont);
topMargin += ( fm.height() + SPACING_SIZE );
}
if (myShowAxesTitle && !myXTitle.isEmpty()) {
QFontMetrics fm(myAxesTitleFont);
bottomMargin += ( fm.height() + SPACING_SIZE );
}
if (myShowAxesTitle && !myYTitle.isEmpty()) {
QFontMetrics fm(myAxesTitleFont);
topMargin += ( fm.height() + SPACING_SIZE );
}
if (myShowTickMarks) {
QFontMetrics fm(myTickMarksFont);
rightMargin += SPACING_SIZE * 3;
leftMargin += SPACING_SIZE * 10;
bottomMargin += ( fm.height() + SPACING_SIZE );
topMargin += fm.height() / 2 + SPACING_SIZE;
}
}
//================================================================
// Function : GraphView::insertItem
// Purpose : inserts item into list
//================================================================
void GraphView::insertItem(GraphItem* item) {
int aMarker, aColor, aLine;
getNextMarker(aMarker, aColor, aLine);
item->setMarker(aMarker, aColor, aLine);
myItems.append(item);
update(); //repaint(false);
if (myLegend)
myLegend->updateMe();
}
//================================================================
// Function : GraphView::removeItem
// Purpose : removes item from list
//================================================================
void GraphView::removeItem(const int index) {
myItems.removeAt(index);
update(); //repaint(false);
if (myLegend)
myLegend->updateMe();
}
//================================================================
// Function : GraphView::clear
// Purpose : clears data list
//================================================================
void GraphView::clear() {
myItems.clear();
update(); //repaint(false);
if (myLegend)
myLegend->updateMe();
}
//================================================================
// Function : GraphView::getItem
// Purpose : gets item by index, returns NULL if index is out of range
//================================================================
GraphItem* GraphView::getItem(const int index) {
return myItems.at(index);
}
//================================================================
// Function : GraphView::findItem
// Purpose : returns index of item in data list
//================================================================
int GraphView::findItem(GraphItem* item) {
return myItems.indexOf(item);
}
//================================================================
// Function : GraphView::setSorted
// Purpose : sets/unsets items to be sorted by horizontal coordinate
//================================================================
void GraphView::setSorted(bool sorted) {
GraphItem* item;
foreach (item, myItems) {
item->setSorted(sorted);
}
update(); //repaint(false);
}
//================================================================
// Function : GraphView::getRange
// Purpose : gets full data range
//================================================================
void GraphView::getRange(double& minX, double& minY,
double& maxX, double& maxY) {
minX = DBL_MAX;
maxX = -DBL_MAX;
minY = DBL_MAX;
maxY = -DBL_MAX;
GraphItem* item;
int cnt = 0;
foreach (item, myItems) {
int i;
cnt += item->count();
for (i = 0; i < item->count(); i++) {
GraphNode* node = item->getNode(i);
if (node->x() < minX)
minX = node->x();
if (node->x() > maxX)
maxX = node->x();
if (node->y() < minY)
minY = node->y();
if (node->y() > maxY)
maxY = node->y();
}
}
if (cnt <= 0) {
minX = 0.0;
minY = 0.0;
maxX = 0.0;
maxY = 0.0;
}
}
//================================================================
// Function : GraphView::getCurrentRange
// Purpose : gets current data range
//================================================================
void GraphView::getCurrentRange(double& minX, double& minY,
double& maxX, double& maxY) {
minX = myOrigin.x();
minY = myOrigin.y();
QRect pRect = getGraphRect();
maxX = minX + myXScale * (pRect.width() - 1);
maxY = minY + myYScale * (pRect.height() - 1);
}
//================================================================
// Function : GraphView::getNbColors
// Purpose : returns number of colors provided
// default value is 16
//================================================================
int GraphView::getNbColors(){
return myColors.count();
}
//================================================================
// Function : GraphView::getNbMarkers
// Purpose : returns number of markers provided
// default value is 11 :
// 0 - square
// 1 - circle
// 2 - triangle (looks up)
// 3 - triangle (looks down)
// 4 - cross
// 5 - rhomb
// 6 - empty square
// 7 - empty circle
// 8 - empty rhomb
// 9 - empty triangle (looks up)
// 9 - empty triangle (looks down)
//================================================================
int GraphView::getNbMarkers() {
return 11;
}
//================================================================
// Function : GraphView::getNbTypeLines
// Purpose : returns number of type lines provided
// default value is 5 :
// 0 - SolidLine
// 1 - DashLine
// 2 - DotLine
// 3 - DashDotLine
// 4 - DashDotDotLine
//================================================================
int GraphView::getNbTypeLines() {
return myLines.count();
}
//================================================================
// Function : GraphView::setMarkerSize
// Purpose : sets marker size
//================================================================
void GraphView::setMarkerSize(const int size) {
myMarkerSize = size;
update(); //repaint(false);
if (myLegend)
myLegend->updateMe();
}
//================================================================
// Function : GraphView::tip
// Purpose : handles tooltips for the view
//================================================================
QRect GraphView::tip(const QPoint& point, QString& tipText) {
if (!myShowTooltips/* || !myShowMarkers */)
return QRect(0, 0, -1, -1);
QRect where(QPoint(0,0), QSize(getMarkerSize(), getMarkerSize()));
where.moveCenter(point);
unsigned i;
QRect pRect = this->getGraphRect();
long minLength = -1;
QListIterator<GraphItem*> iter(myItems);
iter.toBack();
while (iter.hasPrevious()) {
GraphItem* aItem = iter.previous();
unsigned aNbNodes = aItem->count();
for (i = 0; i < aNbNodes; i++) {
GraphNode* node = aItem->getNode(i);
long x = (long)((node->x() - myOrigin.x()) / myXScale);
long y = (long)((node->y() - myOrigin.y()) / myYScale);
x = pRect.left() + x;
y = pRect.bottom() - y;
QPoint dst = point - QPoint(x, y);
long length = dst.x() * dst.x() + dst.y() * dst.y();
if (where.contains(x, y) && (minLength < 0 || minLength > length)) {
tipText = QString("(") + QString::number(node->x()) + ", " +
QString::number(node->y()) + QString(")");
if (!(aItem->getName().isEmpty()))
tipText = aItem->getName() + " : " + tipText;
minLength = length;
}
}
}
if (!tipText.isEmpty())
return where;
else
return QRect(0, 0, -1, -1);
}
//================================================================
// Function : GraphView::isSameColors [static]
// Purpose : returns true if colors seem to be the same
// (i.e. difference is less than gap)
//================================================================
bool GraphView::isSameColors(const QColor color1,
const QColor color2,
const int gap) {
int rC1, bC1, gC1;
int rC2, bC2, gC2;
color1.getRgb(&rC1, &gC1, &bC1);
color2.getRgb(&rC2, &gC2, &bC2);
return ( abs(rC1 - rC2) + abs(gC1 - gC2) + abs(bC1 - bC2) ) < gap;
}
//================================================================
// Function : GraphView::startOperation
// Purpose : starts/finishes view operation
//================================================================
void GraphView::startOperation(ViewOperation theOp) {
switch (theOp) {
case voZoom:
case voZoomX:
case voZoomY:
case voPan:
case voPanX:
case voPanY:
case voRect:
if (myOperation != voNone) {
startOperation(voNone);
}
myOperation = theOp;
setCursor(getOperationCursor(myOperation));
onStartOperation();
break;
case voNone:
default:
setCursor(myDefCursor);
myOperation = myForcedOp = voNone;
break;
}
}
//================================================================
// Function : GraphView::onStartOperation
// Purpose : called on view operation starting
//================================================================
void GraphView::onStartOperation() {
if (myOperation == voRect) {
if (!myRubberBand)
myRubberBand = new QRubberBand(QRubberBand::Rectangle, this);
QRect r = QRect(myPoint, myOtherPoint).normalized();
myRubberBand->setGeometry( r );
myRubberBand->setVisible(r.isValid());
}
}
//================================================================
// Function : GraphView::onFinishOperation
// Purpose : called on view operation finishing
//================================================================
void GraphView::onFinishOperation()
{
if (myOperation == voRect) {
QRect rect(myPoint, myOtherPoint);
rect = rect.normalized();
if (myRubberBand) myRubberBand->hide();
if (!rect.isValid())
return;
if (rect.width() < mySensibilitySize || rect.height() < mySensibilitySize)
return;
QRect pRect = this->getGraphRect();
int dx = rect.left() - pRect.left();
int dy = rect.bottom() - pRect.bottom();
myOrigin = GraphNode(myOrigin.x() + dx * myXScale,
myOrigin.y() - dy * myYScale);
myXScale = myXScale * rect.width() / pRect.width();
myYScale = myYScale * rect.height() / pRect.height();
update(); //repaint(false);
}
}
//================================================================
// Function : GraphView::onOperation
// Purpose : called on view operation running
//================================================================
void GraphView::onOperation(QPoint mousePos) {
int dx = mousePos.x() - myPoint.x();
int dy = mousePos.y() - myPoint.y();
double dl = sqrt((double)dx * dx + (double)dy * dy);
bool sn = (abs(dx) > abs(dy)) ? (dx > 0) : (dy > 0);
double pw = pow( 2.0, (dl / 100.0 * ( sn ? (1.0) : (-1.0)) ) );
switch (myOperation) {
case voPan:
{
myOrigin = GraphNode(myOrigin.x() - dx * myXScale,
myOrigin.y() + dy * myYScale);
myPoint = mousePos;
update(); //repaint(false);
break;
}
case voPanX:
{
myOrigin = GraphNode(myOrigin.x() - dx * myXScale,
myOrigin.y());
myPoint = mousePos;
update(); //repaint(false);
break;
}
case voPanY:
{
myOrigin = GraphNode(myOrigin.x(),
myOrigin.y() + dy * myYScale);
myPoint = mousePos;
update(); //repaint(false);
break;
}
case voZoom:
{
myXScale /= pw;
myYScale /= pw;
myPoint = mousePos;
update(); //repaint(false);
break;
}
case voZoomX:
{
myXScale /= pow( 2.0, ((double)dx)/100 );
myPoint = mousePos;
update(); //repaint(false);
break;
}
case voZoomY:
{
myYScale /= pow( 2.0, ((double)dy)/100 );
myPoint = mousePos;
update(); //repaint(false);
break;
}
case voRect:
{
myOtherPoint = mousePos;
QRect r = QRect(myPoint, myOtherPoint).normalized();
myRubberBand->setGeometry( r );
myRubberBand->setVisible(r.isValid());
break;
}
}
}
//================================================================
// Function : GraphView::onMouseMove
// Purpose : called on mouse moving action (e.g. for highlighting item)
//================================================================
void GraphView::onMouseMove(QPoint mousePos) {
}
//================================================================
// Function : GraphView::setSensibilitySize
// Purpose : sets sensibility area size
//================================================================
void GraphView::setSensibilitySize(const int size) {
mySensibilitySize = size;
if (mySensibilitySize < MIN_SENSIBILITY_SIZE)
mySensibilitySize = MIN_SENSIBILITY_SIZE;
}
//================================================================
// Function : GraphView::mousePressEvent
// Purpose : MousePress event handler
//================================================================
void GraphView::mousePressEvent( QMouseEvent *e ) {
Qt::MouseButtons aBtn = e->buttons();
Qt::KeyboardModifiers aKey = e->modifiers();
// finishing current viewer operation
if (myOperation != voNone) {
onFinishOperation();
startOperation(voNone);
}
myOtherPoint = myPoint = e->pos();
if (myForcedOp != voNone) {
startOperation(myForcedOp);
}
else {
if (isOperationsEnabled() && isOperationEnabled(testOperation(aBtn, aKey)))
startOperation(testOperation(aBtn, aKey));
}
}
//================================================================
// Function : GraphView::mouseMoveEvent
// Purpose : MouseMove event handler
//================================================================
void GraphView::mouseMoveEvent(QMouseEvent* e) {
if (myOperation != voNone)
onOperation(e->pos());
else
onMouseMove(e->pos());
}
//================================================================
// Function : GraphView::mouseReleaseEvent
// Purpose : MouseRelease event handler
//================================================================
void GraphView::mouseReleaseEvent(QMouseEvent *e) {
// finishing current viewer operation
if (myOperation != voNone) {
onFinishOperation();
startOperation(voNone);
}
}
//================================================================
// Function : GraphView::mouseDoubleClickEvent
// Purpose : MouseDoubleClick event handler
//================================================================
void GraphView::mouseDoubleClickEvent(QMouseEvent *e) {
}
//================================================================
// Function : GraphView::keyPressEvent
// Purpose : KeyPress event handler
//================================================================
void GraphView::keyPressEvent( QKeyEvent *e ) {
// emit vpKeyEvent( e );
}
//================================================================
// Function : GraphView::keyReleaseEvent
// Purpose : KeyRelease event handler
//================================================================
void GraphView::keyReleaseEvent( QKeyEvent *e ) {
// emit vpKeyEvent( e );
}
//================================================================
// Function : GraphView::resizeEvent
// Purpose : Resize event handler
//================================================================
void GraphView::resizeEvent(QResizeEvent* e) {
QWidget::resizeEvent(e);
}
//================================================================
// Function : GraphView::paintEvent
// Purpose : Paint event handler
//================================================================
void GraphView::paintEvent(QPaintEvent* e) {
QPixmap aBuffer = QPixmap(width(), height());
aBuffer.fill(this, QPoint(0,0));
QRect pRect = getGraphRect();
if ( pRect.isValid() ||
pRect.right() == pRect.left() ||
pRect.top() == pRect.bottom() ) {
QPainter p(&aBuffer);
// drawing grid
if (myShowGrid) {
p.save();
drawGrid(&p);
p.restore();
}
// or border
else {
p.save();
drawBorder(&p);
p.restore();
}
// drawing axes
if (myShowAxes) {
p.save();
drawAxes(&p);
p.restore();
}
// drawing tick marks
if (myShowTickMarks) {
p.save();
drawTickMarks(&p);
p.restore();
}
// drawing axes titles
if (myShowAxesTitle) {
p.save();
drawAxesTitle(&p);
p.restore();
}
// drawing title
if (myShowTitle) {
p.save();
drawTitle(&p);
p.restore();
}
// drawing data
GraphItem* aItem;
foreach (aItem, myItems) {
p.save();
drawItem(&p, aItem);
p.restore();
}
}
QPainter painter(this);
painter.drawPixmap(QPoint(0, 0), aBuffer);
}
//================================================================
// Function : GraphView::event
// Purpose : global event handler
//================================================================
bool GraphView::event(QEvent* e)
{
if (e->type() == QEvent::ToolTip) {
QHelpEvent* helpEvent = static_cast<QHelpEvent*>(e);
QString tipText;
QRect r = tip(helpEvent->pos(), tipText);
if (r.isValid() && !tipText.isEmpty() )
QToolTip::showText(helpEvent->globalPos(), tipText, this, r);
else
QToolTip::hideText();
}
return QWidget::event(e);
}
//================================================================
// Function : GraphView::getMargins
// Purpose : gets graph painting area
//================================================================
QRect GraphView::getGraphRect() {
int topMargin;
int bottomMargin;
int leftMargin;
int rightMargin;
getMargins(leftMargin, rightMargin, topMargin, bottomMargin);
return QRect(QPoint(leftMargin, topMargin),
QPoint(width() - rightMargin, height() - bottomMargin));
}
//================================================================
// Function : GraphView::drawGrid
// Purpose : draws grid
//================================================================
void GraphView::drawGrid(QPainter* painter) {
if (!myShowGrid)
return;
// getting painting region
QRect pRect = getGraphRect();
// setting pen
painter->setPen(QPen(Qt::lightGray, 0, Qt::DotLine));
IntList xList, yList;
this->getTickPoints(xList, yList);
unsigned i;
for (i = 0; i < xList.count(); i++) {
painter->drawLine(pRect.left() + xList[i],
pRect.top(),
pRect.left() + xList[i],
pRect.bottom());
}
for (i = 0; i < yList.count(); i++) {
painter->drawLine(pRect.left(),
pRect.bottom() - yList[i],
pRect.right(),
pRect.bottom() - yList[i]);
}
// forcing immediate painting
//painter->flush();
}
//================================================================
// Function : GraphView::drawBorder
// Purpose : draws border (it is drawn when grid is not)
//================================================================
void GraphView::drawBorder(QPainter* painter) {
if (myShowGrid)
return;
// getting painting region
QRect pRect = getGraphRect();
// setting pen
painter->setPen(QPen(Qt::lightGray, 0, Qt::DotLine));
painter->drawLine(pRect.left(),
pRect.top(),
pRect.left(),
pRect.bottom());
painter->drawLine(pRect.left(),
pRect.bottom(),
pRect.right(),
pRect.bottom());
painter->drawLine(pRect.right(),
pRect.top(),
pRect.right(),
pRect.bottom());
painter->drawLine(pRect.left(),
pRect.top(),
pRect.right(),
pRect.top());
// forcing immediate painting
//painter->flush();
}
//================================================================
// Function : GraphView::drawAxes
// Purpose : draws axes
//================================================================
void GraphView::drawAxes(QPainter* painter) {
if (!myShowAxes)
return;
// getting painting region
QRect pRect = getGraphRect();
// setting pen, brush...
painter->setPen(QPen(palette().color(QPalette::Text), 1)); // QPalette::Active,
painter->setBrush(palette().color(QPalette::Text)); // QPalette::Active,
// drawing axes lines
QPoint p1(pRect.left(), pRect.top());
QPoint p2(pRect.left(), pRect.bottom());
QPoint p3(pRect.right(), pRect.bottom());
painter->drawLine(p1, p2);
painter->drawLine(p2, p3);
// drawing arrows
QPolygon ar1(3), ar2(3);
ar1[0] = p1;
ar1[1] = QPoint(pRect.left() - ARROW_WIDTH, pRect.top() + ARROW_LENGTH);
ar1[2] = QPoint(pRect.left() + ARROW_WIDTH, pRect.top() + ARROW_LENGTH);
ar2[0] = p3;
ar2[1] = QPoint(pRect.right() - ARROW_LENGTH, pRect.bottom() + ARROW_WIDTH);
ar2[2] = QPoint(pRect.right() - ARROW_LENGTH, pRect.bottom() - ARROW_WIDTH);
if (pRect.height() > ARROW_LENGTH)
painter->drawPolygon(ar1);
if (pRect.width() > ARROW_LENGTH)
painter->drawPolygon(ar2);
// forcing immediate painting
//painter->flush();
// drawing axes marks
IntList xList, yList;
this->getTickPoints(xList, yList);
unsigned i;
// X axis marks
for (i = 0; i < xList.count(); i++) {
if (pRect.left() + xList[i] != pRect.right() &&
xList[i] != 0) {
painter->drawLine(pRect.left() + xList[i], pRect.bottom() - TICK_SIZE,
pRect.left() + xList[i], pRect.bottom() + TICK_SIZE);
}
}
// Y axis marks
for (i = 0; i < yList.count(); i++) {
if (pRect.bottom() - yList[i] != pRect.top() &&
yList[i] != 0) {
painter->drawLine(pRect.left() - TICK_SIZE, pRect.bottom() - yList[i],
pRect.left() + TICK_SIZE, pRect.bottom() - yList[i]);
}
}
// forcing immediate painting
//painter->flush();
}
//================================================================
// Function : GraphView::drawTickMarks
// Purpose : draws tick marks
//================================================================
void GraphView::drawTickMarks(QPainter* painter) {
if (!myShowTickMarks)
return;
// getting painting region
QRect pRect = getGraphRect();
// setting pen
painter->setPen(QPen(palette().color(QPalette::Text), 1)); //QPalette::Active,
QFontMetrics fm(myTickMarksFont);
painter->setFont(myTickMarksFont);
int aTH = fm.height();
IntList xList, yList;
this->getTickPoints(xList, yList);
unsigned i;
// X axis tick marks
int aLast = 0;
int aFromX, aFromY;
for (i = 0; i < xList.count(); i++) {
QString xOr = QString::number(myOrigin.x() + myXScale * xList[i]);
int aTW = fm.width(xOr);
aFromY = pRect.bottom() + SPACING_SIZE;
aFromX = pRect.left() + xList[i] - aTW/2;
if (aFromX + aTW + SPACING_SIZE > width())
aFromX = width() - aTW - SPACING_SIZE;
if (aFromX < SPACING_SIZE)
aFromX = SPACING_SIZE;
if (aFromX >= aLast) {
painter->drawText(aFromX, aFromY, aTW, aTH, Qt::AlignCenter, xOr);
aLast = aFromX + aTW + SPACING_SIZE * 2;
}
}
// Y axis tick marks
aLast = pRect.bottom() + SPACING_SIZE;
for (i = 0; i < yList.count(); i++) {
QString yOr = QString::number(myOrigin.y() + myYScale * yList[i]);
int aTW = fm.width(yOr);
aFromX = pRect.left() - aTW - SPACING_SIZE;
if (aFromX < SPACING_SIZE)
aFromX = SPACING_SIZE;
aFromY = pRect.bottom() - yList[i] - aTH/2;
if (aFromY + aTH > pRect.bottom() + SPACING_SIZE)
aFromY = pRect.bottom() + SPACING_SIZE - aTH;
if (aFromY < SPACING_SIZE)
aFromY = SPACING_SIZE;
if (aFromY + aTH <= aLast) {
painter->drawText(aFromX, aFromY, aTW, aTH, Qt::AlignCenter, yOr);
aLast = aFromY - SPACING_SIZE * 2;
}
}
// forcing immediate painting
//painter->flush();
}
//================================================================
// Function : GraphView::drawAxesTitle
// Purpose : draws axes titles
//================================================================
void GraphView::drawAxesTitle(QPainter* painter) {
if (!myShowAxesTitle)
return;
// getting painting region
QRect pRect = getGraphRect();
// setting pen
painter->setPen(palette().color(QPalette::Text)); //QPalette::Active,
QFontMetrics fm(myAxesTitleFont);
painter->setFont(myAxesTitleFont);
int aTH = fm.height();
int aFromX, aFromY;
if (!myXTitle.isEmpty()) {
int aTW = fm.width(myXTitle);
aFromX = pRect.right() - aTW/2;
if (aFromX + aTW + SPACING_SIZE > width())
aFromX = width() - aTW - SPACING_SIZE;
aFromY = pRect.bottom() + SPACING_SIZE;
if (myShowTickMarks)
aFromY += (aTH + SPACING_SIZE);
painter->drawText(aFromX, aFromY, aTW, aTH, Qt::AlignCenter, myXTitle);
}
if (!myYTitle.isEmpty()) {
int aTW = fm.width(myYTitle);
aFromX = pRect.left() - aTW/2;
if (aFromX < SPACING_SIZE)
aFromX = SPACING_SIZE;
aFromY = pRect.top() - aTH/2 - SPACING_SIZE - aTH;
painter->drawText(aFromX, aFromY, aTW, aTH, Qt::AlignCenter, myYTitle);
}
// forcing immediate painting
//painter->flush();
}
//================================================================
// Function : GraphView::drawTitle
// Purpose : draws graph title
//================================================================
void GraphView::drawTitle(QPainter* painter) {
if (!myShowTitle)
return;
// getting painting region
QRect pRect = getGraphRect();
// setting pen
painter->setPen(palette().color(QPalette::Text));//QPalette::Active,
QFontMetrics fm(myTitleFont);
painter->setFont(myTitleFont);
int aTH = fm.height();
int aFromX, aFromY;
if (!myTitle.isEmpty()) {
int aTW = fm.width(myTitle);
aFromX = pRect.left() + pRect.width() / 2 - aTW / 2;
if (aFromX + aTW + SPACING_SIZE > width())
aFromX = width() - aTW - SPACING_SIZE;
if (aFromX < SPACING_SIZE)
aFromX = SPACING_SIZE;
aFromY = SPACING_SIZE;
painter->drawText(aFromX, aFromY, aTW, aTH, Qt::AlignCenter, myTitle);
}
// forcing immediate painting
//painter->flush();
}
//================================================================
// Function : GraphView::drawItem
// Purpose : draws item
//================================================================
void GraphView::drawItem(QPainter* painter, GraphItem* item) {
// checking item for validity
if (!item->isValid())
return;
// getting marker, type and color of line
int aMarker = item->getMarker();
QColor aColor = item->getColor();
Qt::PenStyle aLine = item->getLine();
// getting painting region
QRect pRect = getGraphRect();
// setting pen for line
painter->setPen(QPen(aColor, 0, aLine));
int i;
if (item->count() > 1) {
// drawing lines
painter->setClipRect(pRect);
painter->setClipping(true);
for (i = 0; i < item->count() - 1; i++) {
GraphNode* node1 = item->getNode(i);
GraphNode* node2 = item->getNode(i + 1);
long x1 = (long)((node1->x() - myOrigin.x()) / myXScale);
long y1 = (long)((node1->y() - myOrigin.y()) / myYScale);
x1 = pRect.left() + x1;
y1 = pRect.bottom() - y1;
long x2 = (long)((node2->x() - myOrigin.x()) / myXScale);
long y2 = (long)((node2->y() - myOrigin.y()) / myYScale);
x2 = pRect.left() + x2;
y2 = pRect.bottom() - y2;
if (intersects(pRect, x1, y1, x2, y2)) {
painter->drawLine(x1, y1, x2, y2);
}
}
}
painter->setClipping(false);
// drawing markers
painter->save();
if (myShowMarkers) {
for (i = 0; i < item->count(); i++) {
GraphNode* node = item->getNode(i);
long x = (long)((node->x() - myOrigin.x()) / myXScale);
long y = (long)((node->y() - myOrigin.y()) / myYScale);
x = pRect.left() + x;
y = pRect.bottom() - y;
if (pRect.contains(x, y)) {
drawMarker(painter, QPoint(x, y), aMarker, aColor);
}
}
}
else {
bool fLeft = false, fRight = false;
QPoint pLeft, pRight;
for (i = 0; i < item->count(); i++) {
GraphNode* node = item->getNode(i);
long x = (long)((node->x() - myOrigin.x()) / myXScale);
long y = (long)((node->y() - myOrigin.y()) / myYScale);
x = pRect.left() + x;
y = pRect.bottom() - y;
if (pRect.contains(x, y)) {
pLeft = QPoint(x, y);
fLeft = true;
break;
}
}
for (i = item->count()-1; i >= 0; i--) {
GraphNode* node = item->getNode(i);
long x = (long)((node->x() - myOrigin.x()) / myXScale);
long y = (long)((node->y() - myOrigin.y()) / myYScale);
x = pRect.left() + x;
y = pRect.bottom() - y;
if (pRect.contains(x, y)) {
pRight = QPoint(x, y);
fRight = true;
break;
}
}
if (fLeft)
drawMarker(painter, pLeft, aMarker, aColor);
if (fRight && pLeft != pRight)
drawMarker(painter, pRight, aMarker, aColor);
}
painter->restore();
// forcing immediate painting
//painter->flush();
}
#ifdef DEB
//================================================================
// Function : GraphView::drawItem
// Purpose : draws item
//================================================================
void GraphView::drawItem(QPainter* painter,
int pxMin,
int pxMax,
int pyMin,
int pyMax,
double xMin,
double xMax,
double yMin,
double yMax,
GraphItem* item) {
// checking item for validity
if (!item->isValid())
return;
// getting marker, type and color of line
int aMarker = item->getMarker();
QColor aColor = item->getColor();
PenStyle aLine = item->getLine();
int ml, mr, mt, mb;
getMargins(ml, mr, mt, mb);
// getting painting region
QRect pRect = QRect(QPoint(pxMin+ml, pyMin+mt), QSize(pxMax-pxMin-ml-mr, pyMax-pyMin-mt-mb));
double XScale = (xMax-xMin)/(pxMax-pxMin);
double YScale = (yMax-yMin)/(pyMax-pyMin);
// setting pen for line
painter->setPen(QPen(aColor, 0, aLine));
int i;
if (item->count() > 1) {
// drawing lines
painter->setClipRect(pRect);
painter->setClipping(true);
for (i = 0; i < item->count() - 1; i++) {
GraphNode* node1 = item->getNode(i);
GraphNode* node2 = item->getNode(i + 1);
long x1 = (long)((node1->x() - xMin) / XScale);
long y1 = (long)((node1->y() - yMin) / YScale);
x1 = pRect.left() + x1;
y1 = pRect.bottom() - y1;
long x2 = (long)((node2->x() - xMin) / XScale);
long y2 = (long)((node2->y() - yMin) / YScale);
x2 = pRect.left() + x2;
y2 = pRect.bottom() - y2;
if (intersects(pRect, x1, y1, x2, y2)) {
painter->drawLine(x1, y1, x2, y2);
}
}
painter->setClipping(false);
// drawing markers
painter->save();
if (myShowMarkers) {
for (i = 0; i < item->count(); i++) {
GraphNode* node = item->getNode(i);
long x = (long)((node->x() - xMin) / XScale);
long y = (long)((node->y() - yMin) / YScale);
x = pRect.left() + x;
y = pRect.bottom() - y;
if (pRect.contains(x, y)) {
drawMarker(painter, QPoint(x, y), aMarker, aColor);
}
}
}
else {
bool fLeft = false, fRight = false;
QPoint pLeft, pRight;
for (i = 0; i < item->count(); i++) {
GraphNode* node = item->getNode(i);
long x = (long)((node->x() - xMin) / XScale);
long y = (long)((node->y() - yMin) / YScale);
x = pRect.left() + x;
y = pRect.bottom() - y;
if (pRect.contains(x, y)) {
pLeft = QPoint(x, y);
fLeft = true;
break;
}
}
for (i = item->count()-1; i >= 0; i--) {
GraphNode* node = item->getNode(i);
long x = (long)((node->x() - xMin) / XScale);
long y = (long)((node->y() - yMin) / YScale);
x = pRect.left() + x;
y = pRect.bottom() - y;
if (pRect.contains(x, y)) {
pRight = QPoint(x, y);
fRight = true;
break;
}
}
if (fLeft)
drawMarker(painter, pLeft, aMarker, aColor);
if (fRight && pLeft != pRight)
drawMarker(painter, pRight, aMarker, aColor);
}
painter->restore();
}
// forcing immediate painting
//painter->flush();
}
#endif
//================================================================
// Function : GraphView::drawMarker
// Purpose : draws marker at position
//================================================================
void GraphView::drawMarker(QPainter* painter,
const QPoint point,
const int marker,
const QColor& color) {
if (marker < 0)
return;
// pen
QPen pen(color, 0, Qt::SolidLine);
// drawing brush
QBrush brush(pen.color());
// marker rect
int size = getMarkerSize();
QRect mRect(QPoint(0,0), QSize(size, size));
mRect.moveCenter(point);
painter->setPen(pen);
painter->setBrush(pen.color());
switch(marker) {
case 0: // 0 - square
{
painter->drawRect(mRect);
}
break;
case 1: // 1 - circle
{
painter->drawEllipse(mRect);
}
break;
case 2: // 2 - triangle (looks up)
{
QPolygon ar(3);
double d = sqrt(3.0);
double d1 = (double)size / (2 * d);
double d2 = (double)size * d / 3;
ar[0] = QPoint(point.x() - size/2, (int)((double)point.y() + d1));
ar[1] = QPoint(point.x(), (int)((double)point.y() - d2));
ar[2] = QPoint(point.x() + size/2, (int)((double)point.y() + d1));
painter->drawPolygon(ar);
}
break;
case 3: // 3 - triangle (looks down)
{
QPolygon ar(3);
double d = sqrt(3.0);
double d1 = (double)size / (2 * d);
double d2 = (double)size * d / 3;
ar[0] = QPoint(point.x() - size/2, (int)((double)point.y() - d1));
ar[1] = QPoint(point.x(), (int)((double)point.y() + d2));
ar[2] = QPoint(point.x() + size/2, (int)((double)point.y() - d1));
painter->drawPolygon(ar);
}
break;
case 4: // 4 - cross
{
painter->drawLine(mRect.left(), mRect.top(),
mRect.right(), mRect.bottom());
painter->drawLine(mRect.left(), mRect.bottom(),
mRect.right(), mRect.top());
}
break;
case 5: // 5 - rhomb
{
QPolygon ar(4);
ar[0] = QPoint(point.x(), mRect.top());
ar[1] = QPoint(mRect.right(), point.y());
ar[2] = QPoint(point.x(), mRect.bottom());
ar[3] = QPoint(mRect.left(), point.y());
painter->drawPolygon(ar);
}
break;
case 6: // 6 - empty square
{
painter->drawLine(mRect.left(), mRect.top(),
mRect.right(), mRect.top());
painter->drawLine(mRect.right(), mRect.top(),
mRect.right(), mRect.bottom());
painter->drawLine(mRect.right(), mRect.bottom(),
mRect.left(), mRect.bottom());
painter->drawLine(mRect.left(), mRect.bottom(),
mRect.left(), mRect.top());
}
break;
case 7: // 7 - empty circle
{
painter->drawArc(mRect, 0, 16 * 360);
}
break;
case 8: // 8 - empty rhomb
{
painter->drawLine(point.x(), mRect.top(),
mRect.right(), point.y());
painter->drawLine(mRect.right(), point.y(),
point.x(), mRect.bottom());
painter->drawLine(point.x(), mRect.bottom(),
mRect.left(), point.y());
painter->drawLine(mRect.left(), point.y(),
point.x(), mRect.top());
}
break;
case 9: // 9 - empty triangle (looks up)
{
QPolygon ar(3);
double d = sqrt(3.0);
double d1 = (double)size / (2 * d);
double d2 = (double)size * d / 3;
painter->drawLine(point.x() - size/2, (int)((double)point.y() + d1),
point.x(), (int)((double)point.y() - d2));
painter->drawLine(point.x(), (int)((double)point.y() - d2),
point.x() + size/2, (int)((double)point.y() + d1));
painter->drawLine(point.x() + size/2, (int)((double)point.y() + d1),
point.x() - size/2, (int)((double)point.y() + d1));
}
break;
case 10: // 10 - empty triangle (looks down)
{
QPolygon ar(3);
double d = sqrt(3.0);
double d1 = (double)size / (2 * d);
double d2 = (double)size * d / 3;
painter->drawLine(point.x() + size/2, (int)((double)point.y() - d1),
point.x(), (int)((double)point.y() + d2));
painter->drawLine(point.x(), (int)((double)point.y() + d2),
point.x() - size/2, (int)((double)point.y() - d1));
painter->drawLine(point.x() - size/2, (int)((double)point.y() - d1),
point.x() + size/2, (int)((double)point.y() - d1));
}
break;
default:
break;
}
// forcing immediate painting
//painter->flush();
}
//================================================================
// Function : GraphView::getNextMarker
// Purpose : gets new unique marker for item if possible
//================================================================
void GraphView::getNextMarker(int& typeMarker, int& color, int& typeLine) {
static int aMarker = -1;
static int aColor = -1;
static int aLine = -1;
aMarker = (aMarker + 1) % getNbMarkers();
aColor = (aColor + 1) % getNbColors();
if (aColor == 0)
aColor++;
aLine = (aLine + 1) % getNbTypeLines();
typeMarker = aMarker;
color = aColor;
typeLine = aLine;
if (!existMarker(aMarker, aColor, aLine))
return;
int i, j, k;
for (i = 0; i < getNbMarkers(); i++) {
aMarker = (aMarker + 1) % getNbMarkers();
for (j = 0; j < getNbColors(); j++) {
aColor = (aColor + 1) % getNbColors();
if (aColor == 0)
aColor++;
for (k = 0; k < getNbTypeLines(); k++) {
aLine = (aLine + 1) % getNbTypeLines();
if (!existMarker(aMarker, aColor, aLine)) {
typeMarker = aMarker;
color = aColor;
typeLine = aLine;
return;
}
}
}
}
}
//================================================================
// Function : GraphView::existMarker
// Purpose : checks if marker belongs to any enitity
//================================================================
bool GraphView::existMarker(const int typeMarker, const int color, const int typeLine) {
GraphItem* aItem;
int aMarker, aColor, aLine;
QColor aRgbColor;
foreach (aItem, myItems) {
if (aItem->isValid()) {
aItem->getMarker(aMarker, aColor, aLine);
aRgbColor = aItem->getColor();
if (aItem->hasOwnColor()) {
/* UNCOMMENT THIS CODE TO ALLOW COMPARE DIFFERENT COLORS
if (isSameColors(getColor(aColor), aRgbColor))
return true;
*/
}
else {
if (aMarker == typeMarker &&
aColor == color &&
aLine == typeLine)
return true;
}
}
}
return false;
}
//================================================================
// Function : GraphView::getColor
// Purpose : gets color
//================================================================
QColor GraphView::getColor(const int color) {
if (color >= 0 && color < (int)myColors.count())
return myColors[color];
return QColor(0, 0, 0);
}
//================================================================
// Function : GraphView::getLine
// Purpose : gets line
//================================================================
Qt::PenStyle GraphView::getLine(const int line) {
if (line >= 0 && line < (int)myLines.count())
return myLines[line];
return Qt::NoPen;
}
//================================================================
// Function : intersects
// Purpose : returns true if line [x1,y1] - [x2,y2] intersects rectangle
// or lies inside it
//================================================================
bool GraphView::intersects(QRect rect, long x1, long y1, long x2, long y2)
{
// 1. check for intersecting (boundary rule)
rect = rect.normalized();
QRect lineRect(QPoint(x1, y1), QPoint(x2, y2));
lineRect = lineRect.normalized();
if (!rect.intersects(lineRect))
return false;
// 2. check for hor/vert line
if (x1 == x2 || y1 == y2)
return true;
// 3. check for rectangle corners
int tlX, tlY, trX, trY, blX, blY, brX, brY;
// ... top-left
tlX = (int)((double)(rect.top() - y1) * (x2 - x1) / (y2 - y1) + x1) - rect.left();
tlY = (int)((double)(rect.left() - x1) * (y2 - y1) / (x2 - x1) + y1) - rect.top();
if (tlX == 0 || tlY == 0)
return true;
// ... top-right
trX = (int)((double)(rect.top() - y1) * (x2 - x1) / (y2 - y1) + x1) - rect.right();
trY = (int)((double)(rect.right() - x1) * (y2 - y1) / (x2 - x1) + y1) - rect.top();
if (trX == 0 || trY == 0)
return true;
// ... bottom-left
blX = (int)((double)(rect.bottom() - y1) * (x2 - x1) / (y2 - y1) + x1) - rect.left();
blY = (int)((double)(rect.left() - x1) * (y2 - y1) / (x2 - x1) + y1) - rect.bottom();
if (blX == 0 || blY == 0)
return true;
// ... bottom-right
brX = (int)((double)(rect.bottom() - y1) * (x2 - x1) / (y2 - y1) + x1) - rect.right();
brY = (int)((double)(rect.right() - x1) * (y2 - y1) / (x2 - x1) + y1) - rect.bottom();
if (brX == 0 || brY == 0)
return true;
bool btlX = tlX < 0;
bool btlY = tlY < 0;
bool btrX = trX < 0;
bool btrY = trY < 0;
bool bblX = blX < 0;
bool bblY = blY < 0;
bool bbrX = brX < 0;
bool bbrY = brY < 0;
return !(btlX == btrX && btlX == bblX && btlX == bbrX &&
btlY == btrY && btlY == bblY && btlY == bbrY);
}
//================================================================
// Function : GraphView::getTickPoints
// Purpose : calculates points for grid and/or tick marks
//================================================================
void GraphView::getTickPoints(IntList& xList, IntList& yList) {
// getting painting region
QRect pRect = getGraphRect();
int i;
// for fixed grid
if (myGridMode == gmFixed) {
// calculating X axis ticks
if (myGridPStepX >= MIN_GRID_STEP) {
i = 0;
while(pRect.left() + myGridPStepX * i <= pRect.right()) {
xList.append(myGridPStepX * i);
i++;
}
}
// calculating Y axis ticks
if (myGridPStepY >= MIN_GRID_STEP) {
i = 0;
while(pRect.bottom() - myGridPStepY * i >= pRect.top()) {
yList.append(myGridPStepY * i);
i++;
}
}
}
// for floating grid
else if (myGridMode == gmFloating) {
// drawing vertical lines
double xStep = myGridStepX / myXScale;
if ((int)xStep >= MIN_GRID_STEP) {
i = 0;
while(pRect.left() + (int)(xStep * i) <= pRect.right()) {
xList.append((int)(xStep * i));
i++;
}
}
else {
i = 0;
while(pRect.left() + MIN_GRID_STEP * i <= pRect.right()) {
xList.append(MIN_GRID_STEP * i);
i++;
}
}
// drawing horizontal lines
double yStep = myGridStepY / myYScale;
if ((int)yStep >= MIN_GRID_STEP) {
i = 0;
while(pRect.bottom() - (int)(yStep * i) >= pRect.top()) {
yList.append((int)(yStep * i));
i++;
}
}
else {
i = 0;
while(pRect.bottom() - MIN_GRID_STEP * i >= pRect.top()) {
yList.append(MIN_GRID_STEP * i);
i++;
}
}
}
// for intervals mode
else if (myGridMode == gmIntervals) {
// drawing vertical lines
double xStep = (double)(pRect.width()) / myGridIntX;
if ((int)xStep >= MIN_GRID_STEP) {
i = 0;
while(pRect.left() + (int)(xStep * i) <= pRect.right()) {
xList.append((int)(xStep * i));
i++;
}
}
// drawing horizontal lines
double yStep = (double)(pRect.height()) / myGridIntY;
if ((int)yStep >= MIN_GRID_STEP) {
i = 0;
while(pRect.bottom() - (int)(yStep * i) >= pRect.top()) {
yList.append((int)(yStep * i));
i++;
}
}
}
if (pRect.left() + xList.last() != pRect.right())
xList.append(pRect.right() - pRect.left());
if (pRect.bottom() - yList.last() != pRect.top())
yList.append(pRect.bottom() - pRect.top());
}
//////////////////////////////////////////////////////////////////////
// GraphNode
//////////////////////////////////////////////////////////////////////
//================================================================
// Function : GraphNode::GraphNode
// Purpose : default constructor
//================================================================
GraphNode::GraphNode()
: myX(0),
myY(0) {
}
//================================================================
// Function : GraphNode::GraphNode
// Purpose : constructor
//================================================================
GraphNode::GraphNode(const double x, const double y)
: myX(x),
myY(y) {
}
//================================================================
// Function : GraphNode::GraphNode
// Purpose : copy constructor
//================================================================
GraphNode::GraphNode(const GraphNode& node) {
myX = node.x();
myY = node.y();
}
//================================================================
// Function : GraphNode::operator==
// Purpose : operator==
//================================================================
bool GraphNode::operator== (const GraphNode& node) {
return x() == node.x();
}
//================================================================
// Function : GraphNode::operator<
// Purpose : operator<
//================================================================
bool GraphNode::operator< (const GraphNode& node) {
return x() < node.x();
}
//================================================================
// Function : GraphNode::operator>
// Purpose : operator>
//================================================================
bool GraphNode::operator> (const GraphNode& node) {
return x() > node.x();
}
//================================================================
// Function : GraphNode::operator=
// Purpose : operator=
//================================================================
GraphNode& GraphNode::operator= (const GraphNode& node) {
myX = node.x();
myY = node.y();
return *this;
}
//////////////////////////////////////////////////////////////////////
// GraphItem
//////////////////////////////////////////////////////////////////////
//================================================================
// Function : GraphItem::GraphItem
// Purpose : constructor
//================================================================
GraphItem::GraphItem(GraphView* parent, QString name)
: myParent(parent) {
init();
mySorted = false;
setName(name);
if (myParent)
myParent->insertItem(this);
}
//================================================================
// Function : GraphItem::~GraphItem
// Purpose : destructor
//================================================================
GraphItem::~GraphItem() {
qDeleteAll(myNodes);
}
//================================================================
// Function : GraphItem::addNode
// Purpose : adds node
//================================================================
void GraphItem::addNode(GraphNode* node) {
unsigned i;
if (mySorted) {
for (i = 0; i < myNodes.count(); i++) {
if (*(myNodes.at(i)) > *node) {
myNodes.insert(i, node);
return;
}
}
}
myNodes.append(node);
}
//================================================================
// Function : GraphItem::addNode
// Purpose : adds node
//================================================================
void GraphItem::addNode(const double x, const double y) {
addNode(new GraphNode(x, y));
}
//================================================================
// Function : GraphItem::addNodes
// Purpose : adds nodes
//================================================================
void GraphItem::addNodes(NodeList& nodes) {
for (unsigned i = 0; i < nodes.count(); i++)
addNode(nodes.at(i));
}
//================================================================
// Function : GraphItem::removeNode
// Purpose : removes node (and deletes it)
//================================================================
void GraphItem::removeNode(GraphNode* node) {
myNodes.removeAll(node);
}
//================================================================
// Function : GraphItem::removeNode
// Purpose : removes node by index (and deletes it)
//================================================================
void GraphItem::removeNode(int index) {
removeNode(myNodes.at(index));
}
//================================================================
// Function : GraphItem::clear
// Purpose : clears list of nodes
//================================================================
void GraphItem::clear() {
myNodes.clear();
}
//================================================================
// Function : GraphItem::isValid
// Purpose : returns true if item is valid,
// i.e. has valid attributes and not empty
//================================================================
bool GraphItem::isValid() {
if (isEmpty())
return false;
if (myMarker < 0)
return false;
if (!myHasOwnLine && myLine < 0)
return false;
if (myHasOwnColor && !myOwnColor.isValid() ||
!myHasOwnColor && myColor < 0)
return false;
return true;
}
//================================================================
// Function : GraphItem::setSorted
// Purpose : sets/clears <Sorted> flag (and reorders nodes)
//================================================================
void GraphItem::setSorted(bool sorted) {
if (sorted != mySorted) {
mySorted = sorted;
if (mySorted) {
NodeList oldList(myNodes);
myNodes.clear();
for (unsigned i = 0; i < oldList.count(); i++)
addNode(oldList.at(i));
}
}
}
//================================================================
// Function : GraphItem::getNode
// Purpose : gets node by index
//================================================================
GraphNode* GraphItem::getNode(const int index) {
return myNodes.at(index);
}
//================================================================
// Function : GraphItem::operator[]
// Purpose : gets node by index
//================================================================
GraphNode* GraphItem::operator[](const int index) {
return myNodes.at(index);
}
//================================================================
// Function : GraphItem::setMarker
// Purpose : sets dynamic marker for item
//================================================================
void GraphItem::setMarker(const int marker,
const int color,
const int line) {
myMarker = marker;
myHasOwnMarker = false;
myColor = color;
myHasOwnColor = false;
myLine = line;
myHasOwnLine = false;
}
//================================================================
// Function : GraphItem::setMarker
// Purpose : sets static marker for item
// (with own (NOT CHANGEABLE) marker, color and type line)
//================================================================
void GraphItem::setMarker(const int marker,
const QColor& color,
const Qt::PenStyle line) {
myMarker = marker;
myHasOwnMarker = true;
myOwnColor = color;
myHasOwnColor = true;
myOwnLine = line;
myHasOwnLine = true;
}
//================================================================
// Function : GraphItem::setColor
// Purpose : sets own color
//================================================================
void GraphItem::setColor(const QColor& color) {
myOwnColor = color;
myHasOwnColor = true;
}
//================================================================
// Function : GraphItem::getColor
// Purpose : returns item's color
//================================================================
QColor GraphItem::getColor() {
if (myHasOwnColor)
return myOwnColor;
else
if (myParent)
return myParent->getColor(myColor);
return QColor();
}
//================================================================
// Function : GraphItem::setLine
// Purpose : sets own line
//================================================================
void GraphItem::setLine(const Qt::PenStyle line) {
myOwnLine = line;
myHasOwnLine = true;
}
//================================================================
// Function : GraphItem::getLine
// Purpose : returns item's line type
//================================================================
Qt::PenStyle GraphItem::getLine() {
if (myHasOwnLine)
return myOwnLine;
else
if (myParent)
return myParent->getLine(myLine);
return Qt::SolidLine;
}
//================================================================
// Function : GraphItem::setmarker
// Purpose : sets own marker
//================================================================
void GraphItem::setMarker(const int marker) {
myMarker = marker;
myHasOwnMarker = true;
}
//================================================================
// Function : GraphItem::getMarker
// Purpose : return item's marker type
//================================================================
int GraphItem::getMarker() {
return myMarker;
}
//================================================================
// Function : GraphItem::getMarker
// Purpose : gets marker atributes
//================================================================
void GraphItem::getMarker(int& marker, int& color, int& line) {
marker = myMarker;
color = myColor;
line = myLine;
}
//================================================================
// Function : GraphItem::init
// Purpose : performs initialization
//================================================================
void GraphItem::init() {
// setting invalid marker
setMarker(-1, -1, -1);
myHasOwnMarker = false;
myOwnColor = QColor(255, 255, 255);
myHasOwnColor = false;
myOwnLine = Qt::SolidLine;
myHasOwnLine = false;
}
//////////////////////////////////////////////////////////////////////
// GraphLegend
//////////////////////////////////////////////////////////////////////
#include <qlabel.h>
#define LEGEND_ITEM_SIZE 100
//================================================================
// Function : GraphLegend::GraphLegend
// Purpose : constructor
//================================================================
GraphLegend::GraphLegend(QWidget* parent, GraphView* graph)
: QWidget(parent),
myGraph(graph) {
myGraph->setLegend(this);
setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding));
setMinimumSize(1, 1);
setTitle("LEGEND");
myShowTitle = true;
updateMe();
}
//================================================================
// Function : GraphLegend::drawContents
// Purpose : draws contents
//================================================================
void GraphLegend::paintEvent(QPaintEvent* e)
{
QPainter painter(this);
QColor bgcolor = myGraph->palette().color(myGraph->backgroundRole());
painter.fillRect(e->rect(), bgcolor);
QFontMetrics fm(font());
int aMSize = myGraph->getMarkerSize();
int aTH = MAX(fm.height(), aMSize);
int i;
int aNb = myGraph->getNbItems();
int aFromX;
int aFromY = SPACING_SIZE;
painter.setPen(palette().color(QPalette::Text));//QPalette::Active,
if (myShowTitle && !myTitle.isEmpty()) {
QFontMetrics fmt(myTitleFont);
int aTWT = fmt.width(myTitle);
int aTHT = fmt.height();
aFromX = (width() - aTWT) / 2;
painter.setFont(myTitleFont);
painter.drawText(aFromX,
aFromY,
aTWT,
aTHT,
Qt::AlignCenter,
myTitle);
aFromY += (aTHT + SPACING_SIZE);
}
aFromX = SPACING_SIZE * 2 + MAX(LEGEND_ITEM_SIZE, aMSize);
painter.setFont(font());
for (i = 0; i < aNb; i++) {
GraphItem* item = myGraph->getItem(i);
QColor color = item->getColor();
Qt::PenStyle line = item->getLine();
int marker = item->getMarker();
painter.setPen(QPen(color, 0, line));
painter.drawLine(SPACING_SIZE,
(SPACING_SIZE + aTH ) * i + aFromY + aTH/2,
SPACING_SIZE + LEGEND_ITEM_SIZE,
(SPACING_SIZE + aTH ) * i + aFromY + aTH/2);
myGraph->drawMarker(&painter,
QPoint(SPACING_SIZE + LEGEND_ITEM_SIZE/2,
(SPACING_SIZE + aTH ) * i + aFromY + aTH/2),
marker,
color);
QString aName = item->getName();
painter.setPen(palette().color(QPalette::Text)); //QPalette::Active,
if (aName.isEmpty())
aName = QString("[ Item ") + QString::number(i+1) + QString(" ]");
int aTW = fm.width(aName);
painter.drawText(aFromX,
(SPACING_SIZE + aTH ) * i + aFromY,
aTW,
aTH,
Qt::AlignLeft | Qt::AlignVCenter,
aName);
}
//painter.flush();
}
//================================================================
// Function : GraphLegend::updateMe
// Purpose : updates legend contents
//================================================================
void GraphLegend::updateMe() {
setPalette(myGraph->palette());
//setFont(myGraph->font());
QFont aFont = font();
QFontMetrics fm(aFont);
int i;
int aNb = myGraph->getNbItems();
int xSize = 0;
int ySize = (MAX(fm.height(), myGraph->getMarkerSize()) + SPACING_SIZE) * aNb + SPACING_SIZE;
for (i = 0; i < aNb; i++) {
QString aName = myGraph->getItem(i)->getName();
if (aName.isEmpty())
aName = QString("[ Item ") + QString::number(i+1) + QString(" ]");
int aTW = fm.width(aName);
if (aTW > xSize)
xSize = aTW;
}
xSize += SPACING_SIZE * 3 + MAX(LEGEND_ITEM_SIZE, myGraph->getMarkerSize());
if (myShowTitle && !myTitle.isEmpty()) {
QFontMetrics fmt(myTitleFont);
int aTWT = fmt.width(myTitle);
int aTHT = fmt.height();
xSize = MAX(xSize, (aTWT + SPACING_SIZE * 2));
ySize += (aTHT + SPACING_SIZE);
}
resize(xSize, ySize);
update();
}
//================================================================
// Function : GraphLegend::showTitle
// Purpose : shows/hides legend title
//============================= ===================================
void GraphLegend::showTitle(bool show) {
myShowTitle = show;
updateMe();
}
//================================================================
// Function : GraphLegend::setTitle
// Purpose : sets legend title
//================================================================
void GraphLegend::setTitle(const QString& title) {
myTitle = title;
updateMe();
}
//================================================================
// Function : GraphLegend::tip
// Purpose : handles tooltips for the view
//================================================================
QRect GraphLegend::tip(const QPoint& point, QString& tipText) {
QPoint cPoint = point;//= viewportToContents(point);
GraphItem* item = itemAt(cPoint);
if (!item)
return QRect(0, 0, -1, -1);
QRect tRect = textRect(item, tipText);
QPoint tl = tRect.topLeft();//contentsToViewport(tRect.topLeft());
QPoint br = tRect.bottomRight();//contentsToViewport(tRect.bottomRight());
if (tl.x() < 0 ||
tl.y() < 0 ||
br.x() > width() ||
br.y() > height()) {
return QRect(0, tl.y(), width(), tRect.height());
}
return QRect(0, 0, -1, -1);
}
//================================================================
// Function : GraphLegend::itemAt
// Purpose : returns graph item which is below the point or NULL
//================================================================
GraphItem* GraphLegend::itemAt(const QPoint& point) {
QFont aFont = font();
QFontMetrics fm(aFont);
int aMSize = myGraph->getMarkerSize();
int aTH = MAX(fm.height(), myGraph->getMarkerSize());
int i;
int aFromY = SPACING_SIZE;
if (myShowTitle && !myTitle.isEmpty()) {
QFontMetrics fmt(myTitleFont);
int aTHT = fmt.height();
aFromY += (aTHT + SPACING_SIZE);
}
if (point.y() <= aFromY)
return 0;
for (i = 0; i < myGraph->getNbItems(); i++) {
GraphItem* item = myGraph->getItem(i);
if (point.y() < aFromY + aTH)
return item;
aFromY += (aTH + SPACING_SIZE);
}
return 0;
}
//================================================================
// Function : GraphLegend::textRect
// Purpose : returns rect which contains item's name
//================================================================
QRect GraphLegend::textRect(GraphItem* item, QString& tipText) {
QString aName = item->getName();
int index = myGraph->findItem(item);
if (aName.isEmpty()) {
if (index < 0)
return QRect(0, 0, -1, -1);
aName = QString("[ Item ") + QString::number(index + 1) + QString(" ]");
}
tipText = aName;
QFont aFont = font();
QFontMetrics fm(aFont);
int aTH = MAX(fm.height(), myGraph->getMarkerSize());
int aTW = fm.width(aName);
int aFromY = SPACING_SIZE;
if (myShowTitle && !myTitle.isEmpty()) {
QFontMetrics fmt(myTitleFont);
int aTHT = fmt.height();
aFromY += (aTHT + SPACING_SIZE);
}
aFromY += (aTH + SPACING_SIZE) * index;
return QRect(SPACING_SIZE * 2 + MAX(LEGEND_ITEM_SIZE, myGraph->getMarkerSize()),
aFromY,
aTW,
aTH);
}
//================================================================
// Function : GraphLegend::setTitleFont
// Purpose : sets legend title font
//================================================================
void GraphLegend::setTitleFont(QFont& font) {
myTitleFont = font;
update(); //repaint(false);
}
//================================================================
// Function : GraphLegend::setItemFont
// Purpose : sets item font
//================================================================
void GraphLegend::setItemFont(QFont& font) {
setFont(font);
update(); //repaint(false);
}
//================================================================
// Function : GraphLegend::event
// Purpose : global event handler
//================================================================
bool GraphLegend::event(QEvent* e)
{
if (e->type() == QEvent::ToolTip) {
QHelpEvent* helpEvent = static_cast<QHelpEvent*>(e);
QString tipText;
QRect r = tip(helpEvent->pos(), tipText);
if (r.isValid() && !tipText.isEmpty() )
QToolTip::showText(helpEvent->globalPos(), tipText, this, r);
else
QToolTip::hideText();
}
return QWidget::event(e);
}
//================================================================
// Function : GraphLegend::minimumSizeHint
// Purpose : minimum size
//================================================================
QSize GraphLegend::minimumSizeHint() const
{
return QSize(100, 100);
}
//================================================================
// Function : GraphLegend::sizeHint
// Purpose : size hint
//================================================================
QSize GraphLegend::sizeHint() const
{
return minimumSizeHint();
}
//////////////////////////////////////////////////////////////////////
// GraphSplitView
//////////////////////////////////////////////////////////////////////
//================================================================
// Function : GraphSplitView::GraphSplitView
// Purpose : constructor
//================================================================
GraphSplitView::GraphSplitView(QWidget* parent)
: QWidget(parent), myGraph(0)
{
// creating window layout
QVBoxLayout* aLayout = new QVBoxLayout(this);
aLayout->setMargin(0);
mySplitter = new QSplitter(Qt::Horizontal, this);
aLayout->addWidget(mySplitter);
myGraph = new GraphView(mySplitter);
GraphLegend* legend = new GraphLegend(mySplitter, myGraph);
mySplitter->setStretchFactor(0, 5);
}
//================================================================
// Function : GraphSplitView::getLegend
// Purpose : returns legend view
//================================================================
GraphLegend* GraphSplitView::getLegend()
{
return myGraph ? myGraph->getLegend() : 0;
}
//================================================================
// Function : GraphSplitView::showLegend
// Purpose : shows/hides legend view
//================================================================
void GraphSplitView::showLegend(bool show)
{
getLegend()->setVisible(show);
}
//================================================================
// Function : GraphSplitView::isLegendShown
// Purpose : returns true if legend is being shown
//================================================================
bool GraphSplitView::isLegendShown()
{
return getLegend()->isVisible();
}