375 lines
9.3 KiB
C++
375 lines
9.3 KiB
C++
|
#include "editor.h"
|
||
|
#include <qwt_plot.h>
|
||
|
#include <qwt_plot_canvas.h>
|
||
|
#include <qwt_scale_map.h>
|
||
|
#include <qwt_plot_shapeitem.h>
|
||
|
#include <qevent.h>
|
||
|
|
||
|
class Overlay: public QwtWidgetOverlay
|
||
|
{
|
||
|
public:
|
||
|
Overlay( QWidget *parent, Editor *editor ):
|
||
|
QwtWidgetOverlay( parent ),
|
||
|
d_editor( editor )
|
||
|
{
|
||
|
switch( editor->mode() )
|
||
|
{
|
||
|
case Editor::NoMask:
|
||
|
{
|
||
|
setMaskMode( QwtWidgetOverlay::NoMask );
|
||
|
setRenderMode( QwtWidgetOverlay::AutoRenderMode );
|
||
|
break;
|
||
|
}
|
||
|
case Editor::Mask:
|
||
|
{
|
||
|
setMaskMode( QwtWidgetOverlay::MaskHint );
|
||
|
setRenderMode( QwtWidgetOverlay::AutoRenderMode );
|
||
|
break;
|
||
|
}
|
||
|
case Editor::AlphaMask:
|
||
|
{
|
||
|
setMaskMode( QwtWidgetOverlay::AlphaMask );
|
||
|
setRenderMode( QwtWidgetOverlay::AutoRenderMode );
|
||
|
break;
|
||
|
}
|
||
|
case Editor::AlphaMaskRedraw:
|
||
|
{
|
||
|
setMaskMode( QwtWidgetOverlay::AlphaMask );
|
||
|
setRenderMode( QwtWidgetOverlay::DrawOverlay );
|
||
|
break;
|
||
|
}
|
||
|
case Editor::AlphaMaskCopyMask:
|
||
|
{
|
||
|
setMaskMode( QwtWidgetOverlay::AlphaMask );
|
||
|
setRenderMode( QwtWidgetOverlay::CopyAlphaMask );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
virtual void drawOverlay( QPainter *painter ) const
|
||
|
{
|
||
|
d_editor->drawOverlay( painter );
|
||
|
}
|
||
|
|
||
|
virtual QRegion maskHint() const
|
||
|
{
|
||
|
return d_editor->maskHint();
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
Editor *d_editor;
|
||
|
};
|
||
|
|
||
|
Editor::Editor( QwtPlot* plot ):
|
||
|
QObject( plot ),
|
||
|
d_isEnabled( false ),
|
||
|
d_overlay( NULL ),
|
||
|
d_mode( Mask )
|
||
|
{
|
||
|
setEnabled( true );
|
||
|
}
|
||
|
|
||
|
|
||
|
Editor::~Editor()
|
||
|
{
|
||
|
delete d_overlay;
|
||
|
}
|
||
|
|
||
|
QwtPlot *Editor::plot()
|
||
|
{
|
||
|
return qobject_cast<QwtPlot *>( parent() );
|
||
|
}
|
||
|
|
||
|
const QwtPlot *Editor::plot() const
|
||
|
{
|
||
|
return qobject_cast<const QwtPlot *>( parent() );
|
||
|
}
|
||
|
|
||
|
void Editor::setMode( Mode mode )
|
||
|
{
|
||
|
d_mode = mode;
|
||
|
}
|
||
|
|
||
|
Editor::Mode Editor::mode() const
|
||
|
{
|
||
|
return d_mode;
|
||
|
}
|
||
|
|
||
|
void Editor::setEnabled( bool on )
|
||
|
{
|
||
|
if ( on == d_isEnabled )
|
||
|
return;
|
||
|
|
||
|
QwtPlot *plot = qobject_cast<QwtPlot *>( parent() );
|
||
|
if ( plot )
|
||
|
{
|
||
|
d_isEnabled = on;
|
||
|
|
||
|
if ( on )
|
||
|
{
|
||
|
plot->canvas()->installEventFilter( this );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
plot->canvas()->removeEventFilter( this );
|
||
|
|
||
|
delete d_overlay;
|
||
|
d_overlay = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool Editor::isEnabled() const
|
||
|
{
|
||
|
return d_isEnabled;
|
||
|
}
|
||
|
|
||
|
bool Editor::eventFilter( QObject* object, QEvent* event )
|
||
|
{
|
||
|
QwtPlot *plot = qobject_cast<QwtPlot *>( parent() );
|
||
|
if ( plot && object == plot->canvas() )
|
||
|
{
|
||
|
switch( event->type() )
|
||
|
{
|
||
|
case QEvent::MouseButtonPress:
|
||
|
{
|
||
|
const QMouseEvent* mouseEvent =
|
||
|
dynamic_cast<QMouseEvent* >( event );
|
||
|
|
||
|
if ( d_overlay == NULL &&
|
||
|
mouseEvent->button() == Qt::LeftButton )
|
||
|
{
|
||
|
const bool accepted = pressed( mouseEvent->pos() );
|
||
|
if ( accepted )
|
||
|
{
|
||
|
d_overlay = new Overlay( plot->canvas(), this );
|
||
|
|
||
|
d_overlay->updateOverlay();
|
||
|
d_overlay->show();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case QEvent::MouseMove:
|
||
|
{
|
||
|
if ( d_overlay )
|
||
|
{
|
||
|
const QMouseEvent* mouseEvent =
|
||
|
dynamic_cast< QMouseEvent* >( event );
|
||
|
|
||
|
const bool accepted = moved( mouseEvent->pos() );
|
||
|
if ( accepted )
|
||
|
d_overlay->updateOverlay();
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case QEvent::MouseButtonRelease:
|
||
|
{
|
||
|
const QMouseEvent* mouseEvent =
|
||
|
static_cast<QMouseEvent* >( event );
|
||
|
|
||
|
if ( d_overlay && mouseEvent->button() == Qt::LeftButton )
|
||
|
{
|
||
|
released( mouseEvent->pos() );
|
||
|
|
||
|
delete d_overlay;
|
||
|
d_overlay = NULL;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return QObject::eventFilter( object, event );
|
||
|
}
|
||
|
|
||
|
bool Editor::pressed( const QPoint& pos )
|
||
|
{
|
||
|
d_editedItem = itemAt( pos );
|
||
|
if ( d_editedItem )
|
||
|
{
|
||
|
d_currentPos = pos;
|
||
|
setItemVisible( d_editedItem, false );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false; // don't accept the position
|
||
|
}
|
||
|
|
||
|
bool Editor::moved( const QPoint& pos )
|
||
|
{
|
||
|
if ( plot() == NULL )
|
||
|
return false;
|
||
|
|
||
|
const QwtScaleMap xMap = plot()->canvasMap( d_editedItem->xAxis() );
|
||
|
const QwtScaleMap yMap = plot()->canvasMap( d_editedItem->yAxis() );
|
||
|
|
||
|
const QPointF p1 = QwtScaleMap::invTransform( xMap, yMap, d_currentPos );
|
||
|
const QPointF p2 = QwtScaleMap::invTransform( xMap, yMap, pos );
|
||
|
|
||
|
#if QT_VERSION >= 0x040600
|
||
|
const QPainterPath shape = d_editedItem->shape().translated( p2 - p1 );
|
||
|
#else
|
||
|
const double dx = p2.x() - p1.x();
|
||
|
const double dy = p2.y() - p1.y();
|
||
|
|
||
|
QPainterPath shape = d_editedItem->shape();
|
||
|
for ( int i = 0; i < shape.elementCount(); i++ )
|
||
|
{
|
||
|
const QPainterPath::Element &el = shape.elementAt( i );
|
||
|
shape.setElementPositionAt( i, el.x + dx, el.y + dy );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
d_editedItem->setShape( shape );
|
||
|
d_currentPos = pos;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void Editor::released( const QPoint& pos )
|
||
|
{
|
||
|
Q_UNUSED( pos );
|
||
|
|
||
|
if ( d_editedItem )
|
||
|
{
|
||
|
raiseItem( d_editedItem );
|
||
|
setItemVisible( d_editedItem, true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QwtPlotShapeItem* Editor::itemAt( const QPoint& pos ) const
|
||
|
{
|
||
|
const QwtPlot *plot = this->plot();
|
||
|
if ( plot == NULL )
|
||
|
return NULL;
|
||
|
|
||
|
// translate pos into the plot coordinates
|
||
|
double coords[ QwtPlot::axisCnt ];
|
||
|
coords[ QwtPlot::xBottom ] =
|
||
|
plot->canvasMap( QwtPlot::xBottom ).invTransform( pos.x() );
|
||
|
coords[ QwtPlot::xTop ] =
|
||
|
plot->canvasMap( QwtPlot::xTop ).invTransform( pos.x() );
|
||
|
coords[ QwtPlot::yLeft ] =
|
||
|
plot->canvasMap( QwtPlot::yLeft ).invTransform( pos.y() );
|
||
|
coords[ QwtPlot::yRight ] =
|
||
|
plot->canvasMap( QwtPlot::yRight ).invTransform( pos.y() );
|
||
|
|
||
|
QwtPlotItemList items = plot->itemList();
|
||
|
for ( int i = items.size() - 1; i >= 0; i-- )
|
||
|
{
|
||
|
QwtPlotItem *item = items[ i ];
|
||
|
if ( item->isVisible() &&
|
||
|
item->rtti() == QwtPlotItem::Rtti_PlotShape )
|
||
|
{
|
||
|
QwtPlotShapeItem *shapeItem = static_cast<QwtPlotShapeItem *>( item );
|
||
|
const QPointF p( coords[ item->xAxis() ], coords[ item->yAxis() ] );
|
||
|
|
||
|
if ( shapeItem->boundingRect().contains( p )
|
||
|
&& shapeItem->shape().contains( p ) )
|
||
|
{
|
||
|
return shapeItem;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
QRegion Editor::maskHint() const
|
||
|
{
|
||
|
return maskHint( d_editedItem );
|
||
|
}
|
||
|
|
||
|
QRegion Editor::maskHint( QwtPlotShapeItem *shapeItem ) const
|
||
|
{
|
||
|
const QwtPlot *plot = this->plot();
|
||
|
if ( plot == NULL || shapeItem == NULL )
|
||
|
return QRegion();
|
||
|
|
||
|
const QwtScaleMap xMap = plot->canvasMap( shapeItem->xAxis() );
|
||
|
const QwtScaleMap yMap = plot->canvasMap( shapeItem->yAxis() );
|
||
|
|
||
|
QRect rect = QwtScaleMap::transform( xMap, yMap,
|
||
|
shapeItem->shape().boundingRect() ).toRect();
|
||
|
|
||
|
const int m = 5; // some margin for the pen
|
||
|
return rect.adjusted( -m, -m, m, m );
|
||
|
}
|
||
|
|
||
|
void Editor::drawOverlay( QPainter* painter ) const
|
||
|
{
|
||
|
const QwtPlot *plot = this->plot();
|
||
|
if ( plot == NULL || d_editedItem == NULL )
|
||
|
return;
|
||
|
|
||
|
const QwtScaleMap xMap = plot->canvasMap( d_editedItem->xAxis() );
|
||
|
const QwtScaleMap yMap = plot->canvasMap( d_editedItem->yAxis() );
|
||
|
|
||
|
painter->setRenderHint( QPainter::Antialiasing,
|
||
|
d_editedItem->testRenderHint( QwtPlotItem::RenderAntialiased ) );
|
||
|
d_editedItem->draw( painter, xMap, yMap,
|
||
|
plot->canvas()->contentsRect() );
|
||
|
}
|
||
|
|
||
|
void Editor::raiseItem( QwtPlotShapeItem *shapeItem )
|
||
|
{
|
||
|
const QwtPlot *plot = this->plot();
|
||
|
if ( plot == NULL || shapeItem == NULL )
|
||
|
return;
|
||
|
|
||
|
const QwtPlotItemList items = plot->itemList();
|
||
|
for ( int i = items.size() - 1; i >= 0; i-- )
|
||
|
{
|
||
|
QwtPlotItem *item = items[ i ];
|
||
|
if ( shapeItem == item )
|
||
|
return;
|
||
|
|
||
|
if ( item->isVisible() &&
|
||
|
item->rtti() == QwtPlotItem::Rtti_PlotShape )
|
||
|
{
|
||
|
shapeItem->setZ( item->z() + 1 );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Editor::setItemVisible( QwtPlotShapeItem *item, bool on )
|
||
|
{
|
||
|
if ( plot() == NULL || item == NULL || item->isVisible() == on )
|
||
|
return;
|
||
|
|
||
|
const bool doAutoReplot = plot()->autoReplot();
|
||
|
plot()->setAutoReplot( false );
|
||
|
|
||
|
item->setVisible( on );
|
||
|
|
||
|
plot()->setAutoReplot( doAutoReplot );
|
||
|
|
||
|
/*
|
||
|
Avoid replot with a full repaint of the canvas.
|
||
|
For special combinations - f.e. using the
|
||
|
raster paint engine on a remote display -
|
||
|
this makes a difference.
|
||
|
*/
|
||
|
|
||
|
QwtPlotCanvas *canvas =
|
||
|
qobject_cast<QwtPlotCanvas *>( plot()->canvas() );
|
||
|
if ( canvas )
|
||
|
canvas->invalidateBackingStore();
|
||
|
|
||
|
plot()->canvas()->update( maskHint( item ) );
|
||
|
|
||
|
}
|
||
|
|