File indexing completed on 2024-05-05 03:49:47

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2009-2010 Bastian Holst <bastianholst@gmx.de>
0004 //
0005 
0006 #include "MarbleGraphicsItem.h"
0007 
0008 #include "MarbleGraphicsItem_p.h"
0009 
0010 // Marble
0011 #include "MarbleDebug.h"
0012 #include "ViewportParams.h"
0013 
0014 // Qt
0015 #include <QList>
0016 #include <QPainter>
0017 #include <QPixmap>
0018 #include <QMouseEvent>
0019 
0020 using namespace Marble;
0021 
0022 MarbleGraphicsItem::MarbleGraphicsItem(MarbleGraphicsItemPrivate *dd)
0023     : d_ptr(dd)
0024 {
0025 }
0026 
0027 MarbleGraphicsItem::~MarbleGraphicsItem()
0028 {
0029     delete d_ptr;
0030 }
0031 
0032 bool MarbleGraphicsItem::paintEvent( QPainter *painter, const ViewportParams *viewport )
0033 {
0034     Q_D(MarbleGraphicsItem);
0035 
0036     if (!d->m_visibility) {
0037         return true;
0038     }
0039 
0040     if (d->m_repaintNeeded) {
0041         d->updateChildPositions();
0042         d->m_pixmap = QPixmap();
0043         d->m_repaintNeeded = false;
0044     }
0045 
0046     setProjection( viewport );
0047 
0048     if (d->positions().size() == 0) {
0049         return true;
0050     }
0051 
0052     // At the moment, as GraphicsItems can't be zoomed or rotated ItemCoordinateCache
0053     // and DeviceCoordianteCache is exactly the same
0054     if ( ItemCoordinateCache == cacheMode()
0055          || DeviceCoordinateCache == cacheMode() )
0056     {
0057         const qreal scale = painter->device()->devicePixelRatio();
0058 
0059         const QSize neededPixmapSize = scale * size().toSize() + QSize( 1, 1 ); // adding a pixel for rounding errors
0060 
0061         if (d->m_pixmap.size() != neededPixmapSize ||
0062             d->m_pixmap.devicePixelRatio() != scale) {
0063 
0064 
0065             if ( size().isValid() && !size().isNull() ) {
0066                 d->m_pixmap = QPixmap(neededPixmapSize);
0067                 d->m_pixmap.setDevicePixelRatio(scale);
0068             }
0069             else {
0070                 mDebug() << "Warning: Invalid pixmap size suggested: " << d->m_size;
0071             }
0072 
0073             d->m_pixmap.fill(Qt::transparent);
0074             QPainter pixmapPainter(&d->m_pixmap);
0075             // We paint in best quality here, as we only have to paint once.
0076             pixmapPainter.setRenderHint( QPainter::Antialiasing, true );
0077             // The cache image will get a 0.5 pixel bounding to save antialiasing effects.
0078             pixmapPainter.translate( 0.5, 0.5 );
0079             paint( &pixmapPainter );
0080 
0081             // Paint children
0082             for (MarbleGraphicsItem *item: d->m_children) {
0083                 item->paintEvent( &pixmapPainter, viewport );
0084             }
0085         }
0086 
0087         for (const QPointF& position: d->positions()) {
0088             painter->drawPixmap(position, d->m_pixmap);
0089         }
0090     }
0091     else {
0092         for (const QPointF& position: d->positions()) {
0093             painter->save();
0094 
0095             painter->translate( position );
0096             paint( painter );
0097 
0098             // Paint children
0099             for (MarbleGraphicsItem *item: d->m_children) {
0100                 item->paintEvent( painter, viewport );
0101             }
0102 
0103             painter->restore();
0104         }
0105     }
0106 
0107     return true;
0108 }
0109 
0110 bool MarbleGraphicsItem::contains( const QPointF& point ) const
0111 {
0112     Q_D(const MarbleGraphicsItem);
0113     for (const QRectF& rect: d->boundingRects()) {
0114         if( rect.contains( point ) )
0115             return true;
0116     }
0117     return false;
0118 }
0119 
0120 QVector<QRectF> MarbleGraphicsItemPrivate::boundingRects() const
0121 {
0122     const QVector<QPointF> positions = this->positions();
0123 
0124     QVector<QRectF> list;
0125     list.reserve(positions.count());
0126 
0127     for (const QPointF &point: positions) {
0128         QRectF rect( point, m_size );
0129         if( rect.x() < 0 )
0130             rect.setLeft( 0 );
0131         if( rect.y() < 0 )
0132             rect.setTop( 0 );
0133 
0134         list.append( rect );
0135     }
0136 
0137     return list;
0138 }
0139 
0140 QSizeF MarbleGraphicsItem::size() const
0141 {
0142     Q_D(const MarbleGraphicsItem);
0143     return d->m_size;
0144 }
0145 
0146 AbstractMarbleGraphicsLayout *MarbleGraphicsItem::layout() const
0147 {
0148     Q_D(const MarbleGraphicsItem);
0149     return d->m_layout;
0150 }
0151 
0152 void MarbleGraphicsItem::setLayout( AbstractMarbleGraphicsLayout *layout )
0153 {
0154     Q_D(MarbleGraphicsItem);
0155     // Deleting the old layout
0156     delete d->m_layout;
0157     d->m_layout = layout;
0158     update();
0159 }
0160 
0161 MarbleGraphicsItem::CacheMode MarbleGraphicsItem::cacheMode() const
0162 {
0163     Q_D(const MarbleGraphicsItem);
0164     return d->m_cacheMode;
0165 }
0166 
0167 void MarbleGraphicsItem::setCacheMode( CacheMode mode )
0168 {
0169     Q_D(MarbleGraphicsItem);
0170     d->m_cacheMode = mode;
0171     if (d->m_cacheMode == NoCache) {
0172         d->m_repaintNeeded = true;
0173     }
0174 }
0175 
0176 void MarbleGraphicsItem::update()
0177 {
0178     Q_D(MarbleGraphicsItem);
0179     d->m_repaintNeeded = true;
0180 
0181     // Update the parent.
0182     if (d->m_parent) {
0183         d->m_parent->update();
0184     }
0185 }
0186 
0187 bool MarbleGraphicsItem::visible() const
0188 {
0189     Q_D(const MarbleGraphicsItem);
0190     return d->m_visibility;
0191 }
0192 
0193 void MarbleGraphicsItem::setVisible( bool visible )
0194 {
0195     Q_D(MarbleGraphicsItem);
0196     d->m_visibility = visible;
0197 }
0198 
0199 void MarbleGraphicsItem::hide()
0200 {
0201     setVisible( false );
0202 }
0203 
0204 void MarbleGraphicsItem::show()
0205 {
0206     setVisible( true );
0207 }
0208 
0209 void MarbleGraphicsItem::setSize( const QSizeF& size )
0210 {
0211     Q_D(MarbleGraphicsItem);
0212     if (d->m_size != size) {
0213         d->m_size = size;
0214         update();
0215     }
0216 }
0217 
0218 QSizeF MarbleGraphicsItem::contentSize() const
0219 {
0220     return size();
0221 }
0222 
0223 void MarbleGraphicsItem::setContentSize( const QSizeF& size )
0224 {
0225     setSize( size );
0226 }
0227 
0228 QRectF MarbleGraphicsItem::contentRect() const
0229 {
0230     return QRectF( QPointF( 0, 0 ), contentSize() );
0231 }
0232 
0233 void MarbleGraphicsItem::paint( QPainter *painter )
0234 {
0235     Q_UNUSED( painter );
0236 }
0237 
0238 bool MarbleGraphicsItem::eventFilter( QObject *object, QEvent *e )
0239 {
0240     if ( ! ( e->type() == QEvent::MouseButtonDblClick
0241              || e->type() == QEvent::MouseMove
0242              || e->type() == QEvent::MouseButtonPress
0243              || e->type() == QEvent::MouseButtonRelease ) )
0244     {
0245         return false;
0246     }
0247 
0248     Q_D(const MarbleGraphicsItem);
0249     QMouseEvent *event = static_cast<QMouseEvent*> (e);
0250 
0251     if (!d->m_children.isEmpty()) {
0252         const QVector<QPointF> absolutePositions = d->absolutePositions();
0253 
0254         for( const QPointF& absolutePosition: absolutePositions ) {
0255             QPoint shiftedPos = event->pos() - absolutePosition.toPoint();
0256             
0257             if ( QRect( QPoint( 0, 0 ), size().toSize() ).contains( shiftedPos ) ) {
0258                 for (MarbleGraphicsItem *child: d->m_children) {
0259                     const QVector<QRectF> childRects = child->d_func()->boundingRects();
0260                     
0261                     for( const QRectF& childRect: childRects ) {
0262                         if( childRect.toRect().contains( shiftedPos ) ) {
0263                             if( child->eventFilter( object, e ) ) {
0264                                 return true;
0265                             }
0266                         }
0267                     }
0268                 }
0269             }
0270         }
0271     }
0272 
0273     return false;
0274 }
0275 
0276 void MarbleGraphicsItem::setProjection( const ViewportParams *viewport )
0277 {
0278     Q_D(MarbleGraphicsItem);
0279     d->setProjection(viewport);
0280 }