/////////////////////////////////////////////////////////////////////// // File : Graph.cpp // Created : 26.03.02 // Author : Vadim SANDLER ////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include 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 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(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 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 #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(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(); }