mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 08:38:45 +00:00
441 lines
12 KiB
C++
441 lines
12 KiB
C++
|
|
/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
|
|
* Qwt Widget Library
|
|
* Copyright (C) 1997 Josef Wilgen
|
|
* Copyright (C) 2002 Uwe Rathmann
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the Qwt License, Version 1.0
|
|
*****************************************************************************/
|
|
|
|
#include "IndendPlotMarker.h"
|
|
#include "qwt_symbol.h"
|
|
#include "qwt_plot_marker.h"
|
|
#include "qwt_painter.h"
|
|
#include "qwt_scale_map.h"
|
|
|
|
#include <iostream> // std::cerr
|
|
#include <stdexcept> // std::out_of_range
|
|
|
|
QwtIndPlotMarker::Matrix* QwtIndPlotMarker::m_drawnPixels = new QwtIndPlotMarker::Matrix(1,1);
|
|
|
|
inline QwtIndPlotMarker::Matrix::Matrix(unsigned long rows, unsigned long cols):
|
|
m_rows(rows), m_cols(cols)
|
|
{
|
|
m_data = new bool[rows * cols]();
|
|
}
|
|
|
|
inline QwtIndPlotMarker::Matrix::~Matrix()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
QwtIndPlotMarker::Matrix::Matrix(const Matrix& m) {
|
|
|
|
if((m_rows!=m.rows())||(m_cols!=m.cols())) {
|
|
resize(m.rows(),m.cols());
|
|
}
|
|
|
|
for(unsigned long i=0;i<m.rows();i++){
|
|
for (unsigned long j = 0; j < m.cols(); j++) {
|
|
m_data[i*m_cols+j]=m(i,j);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* UNUSED
|
|
QwtIndPlotMarker::Matrix& QwtIndPlotMarker::Matrix::operator=(const Matrix& m){
|
|
if((m_rows!=m.rows())||(m_cols!=m.cols())) {
|
|
resize(m.rows(),m.cols());
|
|
}
|
|
|
|
for(unsigned long i=0;i<m.rows();i++){
|
|
for (unsigned long j = 0; j < m.cols(); j++) {
|
|
m_data[i*m_cols+j]=m(i,j);
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
|
|
/* bool& QwtIndPlotMarker::Matrix::operator() (unsigned long row, unsigned long col) const */
|
|
/* { */
|
|
/* if ((row >= m_rows || col >= m_cols)||(!m_data)) */
|
|
/* throw std::out_of_range("operator(): invalid index for m_data"); */
|
|
/* return m_data[m_cols*row + col]; */
|
|
/* } */
|
|
|
|
bool QwtIndPlotMarker::Matrix::operator() (unsigned long row, unsigned long col) const
|
|
{
|
|
if ((row >= m_rows || col >= m_cols)||(!m_data))
|
|
throw std::out_of_range("operator(): invalid index for m_data");
|
|
return m_data[m_cols*row + col];
|
|
}
|
|
|
|
inline void QwtIndPlotMarker::Matrix::init() {
|
|
for (unsigned long i = 0; i < m_cols*m_rows; i++) {
|
|
m_data[i] = false;
|
|
}
|
|
}
|
|
|
|
inline void QwtIndPlotMarker::Matrix::reset() {
|
|
delete[] m_data;
|
|
m_data = 0;
|
|
m_rows = 0;
|
|
m_cols = 0;
|
|
}
|
|
|
|
int QwtIndPlotMarker::Matrix::resize(unsigned long row, unsigned long col) {
|
|
delete[] m_data;
|
|
|
|
m_data = new bool[row * col]();
|
|
if(m_data) {
|
|
m_rows = row;
|
|
m_cols = col;
|
|
init();
|
|
return 0;
|
|
}
|
|
else {
|
|
throw std::bad_alloc();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
unsigned long QwtIndPlotMarker::Matrix::rows() const {
|
|
return m_rows;
|
|
}
|
|
|
|
unsigned long QwtIndPlotMarker::Matrix::cols() const {
|
|
return m_cols;
|
|
}
|
|
uintptr_t QwtIndPlotMarker::Matrix::canvasId() const {
|
|
return m_canvasId;
|
|
}
|
|
void QwtIndPlotMarker::Matrix::setCanvasId(uintptr_t vcanvasId){
|
|
m_canvasId = vcanvasId;
|
|
}
|
|
void QwtIndPlotMarker::Matrix::set(unsigned long row, unsigned long col, bool value) {
|
|
m_data[m_rows*row+col] = value;
|
|
}
|
|
|
|
void QwtIndPlotMarker::Matrix::drawnAt(unsigned long row, unsigned long col, unsigned long ysize, unsigned long xsize){
|
|
if((row>=m_rows)||(col>=m_cols))
|
|
return;
|
|
for (unsigned long y = row; (y < (ysize+row))&&(y<m_rows); y++) {
|
|
for (unsigned long x = col; (x < (xsize+col))&&(x<m_cols); x++) {
|
|
m_data[m_cols*y+x] = true;
|
|
}
|
|
}
|
|
}
|
|
bool QwtIndPlotMarker::Matrix::isFree(unsigned long row, unsigned long col, unsigned long ysize, unsigned long xsize) const {
|
|
bool ret = true;
|
|
if((row>=m_rows)||(col>=m_cols))
|
|
return ret;
|
|
for (unsigned long y = row; (y < (ysize+row))&&ret&&(y<m_rows);y++) {
|
|
for (unsigned long x = col; (x < (xsize+col))&&ret&&(x<m_cols); x++) {
|
|
ret = !(m_data[m_cols*y+x]);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
//! Sets alignment to Qt::AlignCenter, and style to QwtIndPlotMarker::NoLine
|
|
QwtIndPlotMarker::QwtIndPlotMarker( const QString &title ):
|
|
QwtPlotMarker(QwtText(title))
|
|
{
|
|
m_spacingX=0;
|
|
m_spacingY=0;
|
|
if(!this->m_drawnPixels) {
|
|
this->m_drawnPixels = new Matrix(1,1);
|
|
}
|
|
}
|
|
|
|
//! Sets alignment to Qt::AlignCenter, and style to QwtIndPlotMarker::NoLine
|
|
QwtIndPlotMarker::QwtIndPlotMarker( const QwtText &title ):
|
|
QwtPlotMarker(title)
|
|
{
|
|
m_spacingX=0;
|
|
m_spacingY=0;
|
|
if(!m_drawnPixels) {
|
|
m_drawnPixels = new Matrix(1,1);
|
|
}
|
|
}
|
|
|
|
//! Destructor
|
|
QwtIndPlotMarker::~QwtIndPlotMarker()
|
|
{
|
|
}
|
|
|
|
/*!
|
|
Draw the marker
|
|
|
|
\param painter Painter
|
|
\param xMap x Scale Map
|
|
\param yMap y Scale Map
|
|
\param canvasRect Contents rectangle of the canvas in painter coordinates
|
|
*/
|
|
void QwtIndPlotMarker::draw( QPainter *painter,
|
|
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
|
|
const QRectF &canvasRect ) //const
|
|
{
|
|
const QPointF pos( xMap.transform( this->xValue() ),
|
|
yMap.transform( this->yValue() ) );
|
|
|
|
// draw lines
|
|
|
|
drawLines( painter, canvasRect, pos );
|
|
|
|
// draw symbol
|
|
if ( this->symbol() &&
|
|
( this->symbol()->style() != QwtSymbol::NoSymbol ) )
|
|
{
|
|
const QSizeF sz = this->symbol()->size();
|
|
|
|
const QRectF clipRect = canvasRect.adjusted(
|
|
-sz.width(), -sz.height(), sz.width(), sz.height() );
|
|
|
|
if ( clipRect.contains( pos ) )
|
|
this->symbol()->drawSymbol( painter, pos );
|
|
}
|
|
|
|
drawLabel( painter, canvasRect, pos );
|
|
}
|
|
|
|
/*!
|
|
Align and draw the text label of the marker
|
|
This is a modified version of the normal PlotMarker drawLabel method.
|
|
It checks first if the space where we want to draw a label is free and if not
|
|
it will indend the label accordingly.
|
|
At the moment this indending is indending is only done if the label is written
|
|
horizontally, in the future we may want to expand the funtionality to vertical labels.
|
|
|
|
\param painter Painter
|
|
\param canvasRect Contents rectangle of the canvas in painter coordinates
|
|
\param pos Position of the marker, translated into widget coordinates
|
|
|
|
\sa drawLabel(), QwtSymbol::drawSymbol()
|
|
*/
|
|
void QwtIndPlotMarker::drawLabel( QPainter *painter,
|
|
const QRectF &canvasRect, const QPointF &pos ) const
|
|
{
|
|
if(reinterpret_cast<uintptr_t>(painter)!=m_drawnPixels->canvasId()){
|
|
m_drawnPixels->setCanvasId(reinterpret_cast<uintptr_t>(painter));
|
|
m_drawnPixels->init();
|
|
}
|
|
//We have a wrong size of the Canvas, it probably was resized, so resize our table too
|
|
if(((*m_drawnPixels).cols()!=canvasRect.width())||((*m_drawnPixels).rows()!=canvasRect.height())){
|
|
m_drawnPixels->resize(canvasRect.height(), canvasRect.width());
|
|
}
|
|
if ( this->label().isEmpty() )
|
|
return;
|
|
|
|
Qt::Alignment align = labelAlignment();
|
|
QPointF alignPos = pos;
|
|
|
|
QSizeF symbolOff( 0, 0 );
|
|
|
|
switch ( this->lineStyle() )
|
|
{
|
|
case QwtPlotMarker::VLine:
|
|
{
|
|
// In VLine-style the y-position is pointless and
|
|
// the alignment flags are relative to the canvas
|
|
|
|
if ( this->labelAlignment() & Qt::AlignTop )
|
|
{
|
|
alignPos.setY( canvasRect.top() );
|
|
align &= ~Qt::AlignTop;
|
|
align |= Qt::AlignBottom;
|
|
}
|
|
else if ( this->labelAlignment() & Qt::AlignBottom )
|
|
{
|
|
// In HLine-style the x-position is pointless and
|
|
// the alignment flags are relative to the canvas
|
|
|
|
alignPos.setY( canvasRect.bottom() - 1 );
|
|
align &= ~Qt::AlignBottom;
|
|
align |= Qt::AlignTop;
|
|
}
|
|
else
|
|
{
|
|
alignPos.setY( canvasRect.center().y() );
|
|
}
|
|
break;
|
|
}
|
|
case QwtPlotMarker::HLine:
|
|
{
|
|
if ( this->labelAlignment() & Qt::AlignLeft )
|
|
{
|
|
alignPos.setX( canvasRect.left() );
|
|
align &= ~Qt::AlignLeft;
|
|
align |= Qt::AlignRight;
|
|
}
|
|
else if ( this->labelAlignment() & Qt::AlignRight )
|
|
{
|
|
alignPos.setX( canvasRect.right() - 1 );
|
|
align &= ~Qt::AlignRight;
|
|
align |= Qt::AlignLeft;
|
|
}
|
|
else
|
|
{
|
|
alignPos.setX( canvasRect.center().x() );
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
if ( this->symbol() &&
|
|
( this->symbol()->style() != QwtSymbol::NoSymbol ) )
|
|
{
|
|
symbolOff = this->symbol()->size() + QSizeF( 1, 1 );
|
|
symbolOff /= 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
qreal pw2 = this->linePen().widthF() / 2.0;
|
|
if ( pw2 == 0.0 )
|
|
pw2 = 0.5;
|
|
|
|
/* const int spacing = this->spacing(); */
|
|
const int spacingX = m_spacingX;
|
|
const int spacingY = m_spacingY;
|
|
|
|
const qreal xOff = qMax( pw2, symbolOff.width() );
|
|
const qreal yOff = qMax( pw2, symbolOff.height() );
|
|
|
|
const QSizeF textSize = this->label().textSize( painter->font() );
|
|
|
|
if ( align & Qt::AlignLeft )
|
|
{
|
|
alignPos.rx() -= xOff + spacingX;
|
|
if ( this->labelOrientation() == Qt::Vertical )
|
|
alignPos.rx() -= textSize.height();
|
|
else
|
|
alignPos.rx() -= textSize.width();
|
|
}
|
|
else if ( align & Qt::AlignRight )
|
|
{
|
|
alignPos.rx() += xOff + spacingX;
|
|
}
|
|
else
|
|
{
|
|
if ( this->labelOrientation() == Qt::Vertical )
|
|
alignPos.rx() -= textSize.height() / 2;
|
|
else
|
|
alignPos.rx() -= textSize.width() / 2;
|
|
}
|
|
|
|
if ( align & Qt::AlignTop )
|
|
{
|
|
alignPos.ry() -= yOff + spacingY;
|
|
if ( this->labelOrientation() != Qt::Vertical )
|
|
alignPos.ry() -= textSize.height();
|
|
}
|
|
else if ( align & Qt::AlignBottom )
|
|
{
|
|
alignPos.ry() += yOff + spacingY;
|
|
if ( this->labelOrientation() == Qt::Vertical )
|
|
alignPos.ry() += textSize.width();
|
|
}
|
|
else
|
|
{
|
|
if ( this->labelOrientation() == Qt::Vertical )
|
|
alignPos.ry() += textSize.width() / 2;
|
|
else
|
|
alignPos.ry() -= textSize.height() / 2;
|
|
}
|
|
//Check to see if the calculated space is free, else shift
|
|
while(!m_drawnPixels->isFree(alignPos.y(),alignPos.x(),textSize.height(),textSize.width())) {
|
|
alignPos.ry() += textSize.height();
|
|
}
|
|
painter->translate( alignPos.x(), alignPos.y() );
|
|
if ( this->labelOrientation() == Qt::Vertical )
|
|
painter->rotate( -90.0 );
|
|
|
|
const QRectF textRect( 0, 0, textSize.width(), textSize.height() );
|
|
this->label().draw( painter, textRect );
|
|
m_drawnPixels->drawnAt(alignPos.y(),alignPos.x(),textSize.height()+spacingY,textSize.width()+spacingX);
|
|
}
|
|
|
|
/*!
|
|
\brief Set the spacing
|
|
|
|
When the label is not centered on the marker position, the spacing
|
|
is the distance between the position and the label.
|
|
|
|
\param spacing Spacing
|
|
\sa spacing(), setLabelAlignment()
|
|
*/
|
|
void QwtIndPlotMarker::setSpacing( int spacing )
|
|
{
|
|
if ( spacing < 0 )
|
|
spacing = 0;
|
|
|
|
if (( spacing == this->spacingX() )&&( spacing == this->spacingY() ))
|
|
return;
|
|
|
|
m_spacingX = spacing;
|
|
m_spacingY = spacing;
|
|
|
|
itemChanged();
|
|
}
|
|
|
|
/*!
|
|
\brief Set the spacing
|
|
|
|
When the label is not centered on the marker position, the spacing
|
|
is the distance between the position and the label.
|
|
|
|
\param spacing Spacing
|
|
\sa spacing(), setLabelAlignment()
|
|
*/
|
|
void QwtIndPlotMarker::setSpacing( int spacingX, int spacingY )
|
|
{
|
|
if ( spacingX < 0 )
|
|
spacingX = 0;
|
|
if ( spacingY < 0 )
|
|
spacingY = 0;
|
|
|
|
|
|
if (( spacingX == this->spacingX() )&&( spacingY == this->spacingY() ))
|
|
return;
|
|
|
|
m_spacingX = spacingX;
|
|
m_spacingY = spacingY;
|
|
|
|
itemChanged();
|
|
}
|
|
|
|
/*!
|
|
\return the spacing
|
|
\sa setSpacing()
|
|
*/
|
|
/* int QwtIndPlotMarker::spacing() const */
|
|
/* { */
|
|
/* return d_data->spacing; */
|
|
/* } */
|
|
|
|
/*!
|
|
\return the spacing X
|
|
\sa setSpacing()
|
|
*/
|
|
int QwtIndPlotMarker::spacingX() const
|
|
{
|
|
return m_spacingX;
|
|
}
|
|
|
|
/*!
|
|
\return the spacing Y
|
|
\sa setSpacing()
|
|
*/
|
|
int QwtIndPlotMarker::spacingY() const
|
|
{
|
|
return m_spacingY;
|
|
}
|
|
|
|
void QwtIndPlotMarker::resetDrawnLabels(){
|
|
m_drawnPixels->init();
|
|
}
|
|
|