File indexing completed on 2024-04-28 03:49:29

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2010 Dennis Nienhüser <nienhueser@kde.org>
0004 //
0005 
0006 #include "RoutingLayer.h"
0007 
0008 #include "GeoDataCoordinates.h"
0009 #include "GeoDataLineString.h"
0010 #include "GeoPainter.h"
0011 #include "MarbleColors.h"
0012 #include "MarblePlacemarkModel.h"
0013 #include "MarbleWidget.h"
0014 #include "MarbleWidgetPopupMenu.h"
0015 #include "RoutingModel.h"
0016 #include "Route.h"
0017 #include "RouteRequest.h"
0018 #include "MarbleModel.h"
0019 #include "AlternativeRoutesModel.h"
0020 #include "RoutingManager.h"
0021 #include "Maneuver.h"
0022 #include "RenderState.h"
0023 
0024 #include <QAbstractItemModel>
0025 #include <QIcon>
0026 #include <QItemSelectionModel>
0027 #include <QAction>
0028 #include <QMouseEvent>
0029 #include <QPixmap>
0030 #include <QFileDialog>
0031 
0032 namespace Marble
0033 {
0034 
0035 class RoutingLayerPrivate
0036 {
0037     template<class T>
0038     struct PaintRegion {
0039         T index;
0040         QRegion region;
0041 
0042         PaintRegion( const T &index_, const QRegion &region_ ) :
0043                 index( index_ ), region( region_ )
0044         {
0045             // nothing to do
0046         }
0047     };
0048 
0049     using ModelRegion = PaintRegion<QModelIndex>;
0050     using RequestRegion = PaintRegion<int>;
0051 
0052 public:
0053     RoutingLayer *const q;
0054 
0055     QList<ModelRegion> m_instructionRegions;
0056 
0057     QList<RequestRegion> m_regions;
0058 
0059     QList<RequestRegion> m_alternativeRouteRegions;
0060 
0061     QList<ModelRegion> m_placemarks;
0062 
0063     QRegion m_routeRegion;
0064 
0065     int m_movingIndex;
0066 
0067     MarbleWidget *const m_marbleWidget;
0068 
0069     QPixmap m_targetPixmap;
0070 
0071     QPixmap m_standardRoutePoint;
0072 
0073     QPixmap m_activeRoutePoint;
0074 
0075     QRect m_dirtyRect;
0076 
0077     QPoint m_dropStopOver;
0078 
0079     QPoint m_dragStopOver;
0080 
0081     int m_dragStopOverRightIndex;
0082 
0083     RoutingModel *const m_routingModel;
0084 
0085     MarblePlacemarkModel *m_placemarkModel;
0086 
0087     QItemSelectionModel *m_selectionModel;
0088 
0089     QSize m_pixmapSize;
0090 
0091     RouteRequest *const m_routeRequest;
0092 
0093     MarbleWidgetPopupMenu *m_contextMenu;
0094 
0095     QAction *m_removeViaPointAction;
0096 
0097     int m_activeMenuIndex;
0098 
0099     AlternativeRoutesModel *const m_alternativeRoutesModel;
0100 
0101     ViewContext m_viewContext;
0102 
0103     bool m_viewportChanged;
0104 
0105     bool m_isInteractive;
0106 
0107     /** Constructor */
0108     explicit RoutingLayerPrivate( RoutingLayer *parent, MarbleWidget *widget );
0109 
0110     /** Update the cached drag position. Use an empty point to clear it. */
0111     void storeDragPosition( const QPoint &position );
0112 
0113     // The following methods are mostly only called at one place in the code, but often
0114     // Inlined to avoid the function call overhead. Having functions here is just to
0115     // keep the code clean
0116 
0117     /** Returns the same color as the given one with its alpha channel adjusted to the given value */
0118     static inline QColor alphaAdjusted( const QColor &color, int alpha );
0119 
0120     static QPixmap createRoutePoint(const QColor &penColor, const QColor &brushColor);
0121 
0122     /**
0123       * Returns the start or destination position if Ctrl key is among the
0124       * provided modifiers, the cached insert position otherwise
0125       */
0126     inline int viaInsertPosition( Qt::KeyboardModifiers modifiers ) const;
0127 
0128     /** Paint icons for each placemark in the placemark model */
0129     inline void renderPlacemarks( GeoPainter *painter );
0130 
0131     /** Paint waypoint polygon */
0132     inline void renderRoute( GeoPainter *painter );
0133 
0134     /** Paint turn instruction for selected items */
0135     inline void renderAnnotations( GeoPainter *painter ) const;
0136 
0137     /** Paint alternative routes in gray */
0138     inline void renderAlternativeRoutes( GeoPainter *painter );
0139 
0140     /** Paint icons for trip points etc */
0141     inline void renderRequest( GeoPainter *painter );
0142 
0143     /** Insert via points or emit position signal, if appropriate */
0144     inline bool handleMouseButtonRelease( QMouseEvent *e );
0145 
0146     /** Select route instructions points, start dragging trip points */
0147     inline bool handleMouseButtonPress( QMouseEvent *e );
0148 
0149     /** Dragging trip points, route polygon hovering */
0150     inline bool handleMouseMove( QMouseEvent *e );
0151 
0152     /** True if the given point (screen coordinates) is among the route instruction points */
0153     inline bool isInfoPoint( const QPoint &point );
0154 
0155     /** True if the given point (screen coordinates) is above an alternative route */
0156     inline bool isAlternativeRoutePoint( const QPoint &point );
0157 
0158     /** Paint the stopover indicator pixmap at the given position. Also repaints the old position */
0159     inline void paintStopOver( QRect position );
0160 
0161     /** Removes the stopover indicator pixmap. Also repaints its old position */
0162     inline void clearStopOver();
0163 };
0164 
0165 RoutingLayerPrivate::RoutingLayerPrivate( RoutingLayer *parent, MarbleWidget *widget ) :
0166         q( parent ), m_movingIndex( -1 ), m_marbleWidget( widget ),
0167         m_targetPixmap(QStringLiteral(":/data/bitmaps/routing_pick.png")),
0168         m_standardRoutePoint(createRoutePoint(widget->model()->routingManager()->routeColorStandard(),
0169                                               widget->model()->routingManager()->routeColorAlternative())),
0170         m_activeRoutePoint(createRoutePoint(widget->model()->routingManager()->routeColorHighlighted(),
0171                                             alphaAdjusted(Oxygen::hotOrange4, 200))),
0172         m_dragStopOverRightIndex(-1),
0173         m_routingModel( widget->model()->routingManager()->routingModel() ),
0174         m_placemarkModel( nullptr ),
0175         m_selectionModel( nullptr ),
0176         m_pixmapSize( 22, 22 ),
0177         m_routeRequest( widget->model()->routingManager()->routeRequest() ),
0178         m_activeMenuIndex( -1 ),
0179         m_alternativeRoutesModel( widget->model()->routingManager()->alternativeRoutesModel() ),
0180         m_viewContext( Still ),
0181         m_viewportChanged( true ),
0182         m_isInteractive( true )
0183 {
0184     m_contextMenu = new MarbleWidgetPopupMenu( m_marbleWidget, m_marbleWidget->model() );
0185     m_removeViaPointAction = new QAction( QObject::tr( "&Remove this Destination" ), q );
0186     QObject::connect( m_removeViaPointAction, SIGNAL(triggered()), q, SLOT(removeViaPoint()) );
0187     m_contextMenu->addAction( Qt::RightButton, m_removeViaPointAction );
0188     QAction *exportAction = new QAction( QObject::tr( "&Export Route..." ), q );
0189     QObject::connect( exportAction, SIGNAL(triggered()), q, SLOT(exportRoute()) );
0190     m_contextMenu->addAction( Qt::RightButton, exportAction );
0191     if ( MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ) {
0192         m_pixmapSize = QSize( 38, 38 );
0193     }
0194 }
0195 
0196 int RoutingLayerPrivate::viaInsertPosition( Qt::KeyboardModifiers modifiers ) const
0197 {
0198     if ( modifiers & Qt::ControlModifier ) {
0199         bool leftHand = m_routeRequest->size() / 2 >= m_dragStopOverRightIndex;
0200         if ( leftHand && m_routeRequest->size() > 2 ) {
0201             return 0;
0202         } else {
0203             return m_routeRequest->size();
0204         }
0205     } else {
0206         return m_dragStopOverRightIndex;
0207     }
0208 }
0209 
0210 void RoutingLayerPrivate::renderPlacemarks( GeoPainter *painter )
0211 {
0212     m_placemarks.clear();
0213     painter->setPen( QColor( Qt::black ) );
0214     for ( int i = 0; i < m_placemarkModel->rowCount(); ++i ) {
0215         QModelIndex index = m_placemarkModel->index( i, 0 );
0216         QVariant data = index.data( MarblePlacemarkModel::CoordinateRole );
0217         if ( index.isValid() && !data.isNull() ) {
0218             GeoDataCoordinates pos = data.value<GeoDataCoordinates>();
0219 
0220             QPixmap pixmap = index.data( Qt::DecorationRole ).value<QPixmap>();
0221             if ( !pixmap.isNull() && m_selectionModel->isSelected( index ) ) {
0222                 QIcon selected = QIcon( pixmap );
0223                 QPixmap result = selected.pixmap( m_pixmapSize, QIcon::Selected, QIcon::On );
0224                 painter->drawPixmap( pos, result );
0225             } else {
0226                 painter->drawPixmap( pos, pixmap );
0227             }
0228 
0229             const QRegion region = painter->regionFromPixmapRect(pos, m_targetPixmap.width(), m_targetPixmap.height());
0230             m_placemarks.push_back( ModelRegion( index, region ) );
0231         }
0232     }
0233 }
0234 
0235 void RoutingLayerPrivate::renderAlternativeRoutes( GeoPainter *painter )
0236 {
0237     QPen alternativeRoutePen( m_marbleWidget->model()->routingManager()->routeColorAlternative() );
0238     alternativeRoutePen.setWidth( 5 );
0239     painter->setPen( alternativeRoutePen );
0240 
0241     for ( int i=0; i<m_alternativeRoutesModel->rowCount(); ++i ) {
0242         const GeoDataDocument *route = m_alternativeRoutesModel->route(i);
0243         if ( route && route != m_alternativeRoutesModel->currentRoute() ) {
0244             const GeoDataLineString* points = AlternativeRoutesModel::waypoints( route );
0245             if ( points ) {
0246                 painter->drawPolyline( *points );
0247                 if ( m_viewportChanged && m_isInteractive && m_viewContext == Still ) {
0248                     QRegion region = painter->regionFromPolyline( *points, 8 );
0249                     m_alternativeRouteRegions.push_back( RequestRegion( i, region ) );
0250                 }
0251             }
0252         }
0253     }
0254 }
0255 
0256 void RoutingLayerPrivate::renderRoute( GeoPainter *painter )
0257 {
0258     GeoDataLineString waypoints = m_routingModel->route().path();
0259 
0260     QPen standardRoutePen( m_marbleWidget->model()->routingManager()->routeColorStandard() );
0261     standardRoutePen.setWidth( 5 );
0262     if ( m_marbleWidget->model()->routingManager()->state() == RoutingManager::Downloading ) {
0263         standardRoutePen.setStyle( Qt::DotLine );
0264     }
0265     painter->setPen( standardRoutePen );
0266 
0267     painter->drawPolyline( waypoints );
0268     if ( m_viewportChanged && m_viewContext == Still ) {
0269         int const offset = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ? 24 : 8;
0270         if ( m_isInteractive ) {
0271             m_routeRegion = painter->regionFromPolyline( waypoints, offset );
0272         }
0273     }
0274 
0275 
0276     standardRoutePen.setWidth( 2 );
0277     painter->setPen( standardRoutePen );
0278 
0279     // Map matched position
0280     //painter->setBrush( QBrush( Oxygen::brickRed4) );
0281     //painter->drawEllipse( m_routingModel->route().positionOnRoute(), 8, 8 );
0282 
0283     painter->setBrush( QBrush( m_marbleWidget->model()->routingManager()->routeColorAlternative() ) );
0284 
0285     if ( !m_dropStopOver.isNull() ) {
0286         int dx = 1 + m_pixmapSize.width() / 2;
0287         int dy = 1 + m_pixmapSize.height() / 2;
0288         QPoint center = m_dropStopOver - QPoint( dx, dy );
0289         painter->drawPixmap( center, m_targetPixmap );
0290 
0291         if ( !m_dragStopOver.isNull() && m_dragStopOverRightIndex >= 0 && m_dragStopOverRightIndex <= m_routeRequest->size() ) {
0292             QPoint moved = m_dropStopOver - m_dragStopOver;
0293             if ( moved.manhattanLength() > 10 ) {
0294                 qreal lon( 0.0 ), lat( 0.0 );
0295                 if ( m_marbleWidget->geoCoordinates( m_dropStopOver.x(), m_dropStopOver.y(),
0296                                                      lon, lat, GeoDataCoordinates::Radian ) ) {
0297                     GeoDataCoordinates drag( lon, lat );
0298                     standardRoutePen.setStyle( Qt::DotLine );
0299                     painter->setPen( standardRoutePen );
0300                     GeoDataLineString lineString;
0301                     if ( m_dragStopOverRightIndex > 0 ) {
0302                         lineString << m_routeRequest->at( m_dragStopOverRightIndex-1 );
0303                     }
0304                     lineString << drag;
0305                     if ( m_dragStopOverRightIndex < m_routeRequest->size() ) {
0306                         lineString << m_routeRequest->at( m_dragStopOverRightIndex );
0307                     }
0308                     painter->drawPolyline( lineString );
0309                     standardRoutePen.setStyle( Qt::SolidLine );
0310                     painter->setPen( standardRoutePen );
0311                 }
0312             }
0313         }
0314     }
0315 
0316     if ( m_viewContext == Animation ) {
0317         return;
0318     }
0319 
0320     if( m_routingModel->rowCount() == m_routingModel->route().size() ) {
0321         m_instructionRegions.clear();
0322 
0323         QPen activeRouteSegmentPen(m_marbleWidget->model()->routingManager()->routeColorHighlighted());
0324         activeRouteSegmentPen.setWidth(6);
0325         if (m_marbleWidget->model()->routingManager()->state() == RoutingManager::Downloading) {
0326             activeRouteSegmentPen.setStyle(Qt::DotLine);
0327         }
0328         painter->setPen(activeRouteSegmentPen);
0329 
0330         for ( int i = 0; i < m_routingModel->rowCount(); ++i ) {
0331             QModelIndex index = m_routingModel->index( i, 0 );
0332             GeoDataCoordinates pos = index.data( MarblePlacemarkModel::CoordinateRole ).value<GeoDataCoordinates>();
0333 
0334             if ( m_selectionModel && m_selectionModel->selection().contains( index ) ) {
0335                 const RouteSegment &segment = m_routingModel->route().at( i );
0336                 const GeoDataLineString currentRoutePoints = segment.path();
0337 
0338                 painter->drawPolyline( currentRoutePoints );
0339 
0340                 painter->drawPixmap(pos, m_activeRoutePoint);
0341             }
0342             else {
0343                 painter->drawPixmap(pos, m_standardRoutePoint);
0344             }
0345 
0346             if ( m_isInteractive ) {
0347                 QRegion region = painter->regionFromEllipse( pos, 12, 12 );
0348                 m_instructionRegions.push_front( ModelRegion( index, region ) );
0349             }
0350         }
0351     }
0352 
0353     if( !m_routingModel->deviatedFromRoute() ) {
0354         GeoDataCoordinates location = m_routingModel->route().currentSegment().nextRouteSegment().maneuver().position();
0355         QString nextInstruction = m_routingModel->route().currentSegment().nextRouteSegment().maneuver().instructionText();
0356         if( !nextInstruction.isEmpty() ) {
0357             painter->drawPixmap(location, m_activeRoutePoint);
0358         }
0359     }
0360 }
0361 
0362 void RoutingLayerPrivate::renderAnnotations( GeoPainter *painter ) const
0363 {
0364     if ( !m_selectionModel || m_selectionModel->selection().isEmpty() ) {
0365         // nothing to do
0366         return;
0367     }
0368 
0369     for ( int i = 0; i < m_routingModel->rowCount(); ++i ) {
0370         QModelIndex index = m_routingModel->index( i, 0 );
0371 
0372         if ( m_selectionModel->selection().contains( index ) ) {
0373             bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
0374             GeoDataCoordinates pos = index.data( MarblePlacemarkModel::CoordinateRole ).value<GeoDataCoordinates>();
0375             painter->setPen( QColor( Qt::black ) );
0376             painter->setBrush( QBrush( Oxygen::sunYellow6 ) );
0377             painter->drawAnnotation( pos, index.data().toString(), QSize( smallScreen ? 240 : 120, 0 ), 10, 30, 5, 5 );
0378         }
0379     }
0380 }
0381 
0382 void RoutingLayerPrivate::renderRequest( GeoPainter *painter )
0383 {
0384     m_regions.clear();
0385     for ( int i = 0; i < m_routeRequest->size(); ++i ) {
0386         const GeoDataCoordinates pos = m_routeRequest->at( i );
0387         if ( pos.isValid() ) {
0388             QPixmap pixmap = m_routeRequest->pixmap( i );
0389             painter->drawPixmap( pos, pixmap );
0390             const QRegion region = painter->regionFromPixmapRect(pos, pixmap.width(), pixmap.height());
0391             m_regions.push_front( RequestRegion( i, region ) );
0392         }
0393     }
0394 }
0395 
0396 void RoutingLayerPrivate::storeDragPosition( const QPoint &pos )
0397 {
0398     m_dragStopOver = pos;
0399     m_dragStopOverRightIndex = -1;
0400 
0401     qreal lon( 0.0 ), lat( 0.0 );
0402     if ( m_routeRequest && !pos.isNull()
0403         && m_marbleWidget->geoCoordinates( pos.x(), pos.y(), lon, lat, GeoDataCoordinates::Radian ) ) {
0404         GeoDataCoordinates waypoint( lon, lat );
0405         m_dragStopOverRightIndex = m_routingModel->rightNeighbor( waypoint, m_routeRequest );
0406     }
0407 }
0408 
0409 QColor RoutingLayerPrivate::alphaAdjusted( const QColor &color, int alpha )
0410 {
0411     QColor result( color );
0412     result.setAlpha( alpha );
0413     return result;
0414 }
0415 
0416 QPixmap RoutingLayerPrivate::createRoutePoint(const QColor &penColor, const QColor &brushColor)
0417 {
0418      QPen pen(penColor);
0419      pen.setWidth(2);
0420 
0421      const QBrush brush(brushColor);
0422 
0423      QPixmap routePoint(QSize(8, 8));
0424      routePoint.fill(Qt::transparent);
0425 
0426      QPainter painter(&routePoint);
0427      painter.setRenderHint(QPainter::Antialiasing, true);
0428      painter.setPen(pen);
0429      painter.setBrush(brush);
0430      painter.drawEllipse(1, 1, 6, 6);
0431 
0432      return routePoint;
0433 }
0434 
0435 bool RoutingLayerPrivate::handleMouseButtonPress( QMouseEvent *e )
0436 {
0437     for( const RequestRegion &region: m_regions ) {
0438         if ( region.region.contains( e->pos() ) ) {
0439             if ( e->button() == Qt::LeftButton ) {
0440                 m_movingIndex = region.index;
0441                 m_dropStopOver = QPoint();
0442                 m_dragStopOver = QPoint();
0443                 return true;
0444             } else if ( e->button() == Qt::RightButton ) {
0445                 m_removeViaPointAction->setEnabled( true );
0446                 m_activeMenuIndex = region.index;
0447                 m_contextMenu->showRmbMenu( e->x(), e->y() );
0448                 return true;
0449             } else
0450                 return false;
0451         }
0452     }
0453 
0454     for( const ModelRegion &region: m_instructionRegions ) {
0455         if ( region.region.contains( e->pos() ) && m_selectionModel ) {
0456             if ( e->button() == Qt::LeftButton ) {
0457                 QItemSelectionModel::SelectionFlag command = QItemSelectionModel::ClearAndSelect;
0458                 if ( m_selectionModel->isSelected( region.index ) ) {
0459                     command = QItemSelectionModel::Clear;
0460                 }
0461                 m_selectionModel->select( region.index, command );
0462                 m_dropStopOver = e->pos();
0463                 storeDragPosition( e->pos() );
0464                 // annotation and old annotation are dirty, large region
0465                 emit q->repaintNeeded();
0466                 return true;
0467             } else if ( e->button() == Qt::RightButton ) {
0468                 m_removeViaPointAction->setEnabled( false );
0469                 m_contextMenu->showRmbMenu( e->x(), e->y() );
0470                 return true;
0471             } else
0472                 return false;
0473         }
0474     }
0475 
0476     if ( m_routeRegion.contains( e->pos() ) ) {
0477         if ( e->button() == Qt::LeftButton ) {
0478             /** @todo: Determine the neighbored via points and insert in order */
0479             m_dropStopOver = e->pos();
0480             storeDragPosition( e->pos() );
0481             return true;
0482         } else if ( e->button() == Qt::RightButton ) {
0483             m_removeViaPointAction->setEnabled( false );
0484             m_contextMenu->showRmbMenu( e->x(), e->y() );
0485             return true;
0486         } else
0487             return false;
0488     }
0489 
0490     if ( e->button() != Qt::LeftButton ) {
0491         return false;
0492     }
0493 
0494     for( const RequestRegion &region: m_alternativeRouteRegions ) {
0495         if ( region.region.contains( e->pos() ) ) {
0496             m_alternativeRoutesModel->setCurrentRoute( region.index );
0497             return true;
0498         }
0499     }
0500 
0501     for( const ModelRegion &region: m_placemarks ) {
0502         if ( region.region.contains( e->pos() ) ) {
0503             emit q->placemarkSelected( region.index );
0504             return true;
0505         }
0506     }
0507 
0508     return false;
0509 }
0510 
0511 bool RoutingLayerPrivate::handleMouseButtonRelease( QMouseEvent *e )
0512 {
0513     if ( e->button() != Qt::LeftButton ) {
0514         return false;
0515     }
0516 
0517     if ( m_movingIndex >= 0 ) {
0518         m_movingIndex = -1;
0519         clearStopOver();
0520         m_marbleWidget->model()->routingManager()->retrieveRoute();
0521         return true;
0522     }
0523 
0524     if ( !m_dropStopOver.isNull() && !m_dragStopOver.isNull() ) {
0525         QPoint moved = e->pos() - m_dragStopOver;
0526         if ( moved.manhattanLength() < 10 ) {
0527             return false;
0528         }
0529 
0530         qreal lon( 0.0 ), lat( 0.0 );
0531         if ( m_dragStopOverRightIndex >= 0 && m_dragStopOverRightIndex <= m_routeRequest->size()
0532                 && m_marbleWidget->geoCoordinates( m_dropStopOver.x(), m_dropStopOver.y(), lon, lat, GeoDataCoordinates::Radian ) ) {
0533             GeoDataCoordinates position( lon, lat );
0534             m_dragStopOverRightIndex = viaInsertPosition( e->modifiers() );
0535             m_routeRequest->insert( m_dragStopOverRightIndex, position );
0536             clearStopOver();
0537             m_marbleWidget->model()->routingManager()->retrieveRoute();
0538             return true;
0539         }
0540     }
0541 
0542     return false;
0543 }
0544 
0545 bool RoutingLayerPrivate::handleMouseMove( QMouseEvent *e )
0546 {
0547     qreal lon( 0.0 ), lat( 0.0 );
0548     if ( m_marbleWidget->geoCoordinates( e->pos().x(), e->pos().y(),
0549                                          lon, lat, GeoDataCoordinates::Radian ) ) {
0550 
0551         if ( m_movingIndex >= 0 ) {
0552             GeoDataCoordinates moved( lon, lat );
0553             m_routeRequest->setPosition( m_movingIndex, moved );
0554             m_marbleWidget->setCursor( Qt::ArrowCursor );
0555         } else if ( !m_dragStopOver.isNull() ) {
0556             // Repaint only that region of the map that is affected by the change
0557             m_dragStopOverRightIndex = viaInsertPosition( e->modifiers() );
0558             QRect dirty = m_routeRegion.boundingRect();
0559             dirty |= QRect( m_dropStopOver, m_pixmapSize );
0560             dirty |= QRect( e->pos(), m_pixmapSize );
0561             if ( e->buttons() & Qt::LeftButton ) {
0562                 m_dropStopOver = e->pos();
0563             } else {
0564                 m_dragStopOver = QPoint();
0565                 m_dropStopOver = QPoint();
0566             }
0567             emit q->repaintNeeded( dirty );
0568             m_marbleWidget->setCursor( Qt::ArrowCursor );
0569         } else if ( isInfoPoint( e->pos() ) ) {
0570             clearStopOver();
0571             m_marbleWidget->setCursor( Qt::ArrowCursor );
0572         } else if ( m_routeRegion.contains( e->pos() ) ) {
0573             m_dropStopOver = e->pos();
0574             m_marbleWidget->setCursor( Qt::ArrowCursor );
0575         } else if ( !m_dropStopOver.isNull() ) {
0576             clearStopOver();
0577         } else if ( isAlternativeRoutePoint( e->pos() ) ) {
0578             m_marbleWidget->setCursor( Qt::ArrowCursor );
0579         }
0580         else {
0581             return false;
0582         }
0583 
0584         // Update pixmap in the map (old and new position needs repaint)
0585         paintStopOver( QRect( e->pos(), m_pixmapSize ) );
0586         return true;
0587     }
0588 
0589     return false;
0590 }
0591 
0592 bool RoutingLayerPrivate::isInfoPoint( const QPoint &point )
0593 {
0594     for( const RequestRegion &region: m_regions ) {
0595         if ( region.region.contains( point ) ) {
0596             return true;
0597         }
0598     }
0599 
0600     for( const ModelRegion &region: m_instructionRegions ) {
0601         if ( region.region.contains( point ) ) {
0602             return true;
0603         }
0604     }
0605 
0606     return false;
0607 }
0608 
0609  bool RoutingLayerPrivate::isAlternativeRoutePoint( const QPoint &point )
0610  {
0611      for( const RequestRegion &region: m_alternativeRouteRegions ) {
0612          if ( region.region.contains( point ) ) {
0613              return true;
0614          }
0615      }
0616 
0617      return false;
0618  }
0619 
0620 void RoutingLayerPrivate::paintStopOver( QRect dirty )
0621 {
0622     emit q->repaintNeeded( m_dirtyRect );
0623     int dx = 1 + m_pixmapSize.width() / 2;
0624     int dy = 1 + m_pixmapSize.height() / 2;
0625     dirty.adjust( -dx, -dy, -dx, -dy );
0626     emit q->repaintNeeded( dirty );
0627     m_dirtyRect = dirty;
0628 }
0629 
0630 void RoutingLayerPrivate::clearStopOver()
0631 {
0632     m_dropStopOver = QPoint();
0633     m_dragStopOver = QPoint();
0634     emit q->repaintNeeded( m_dirtyRect );
0635 }
0636 
0637 RoutingLayer::RoutingLayer( MarbleWidget *widget, QWidget *parent ) :
0638         QObject( parent ), d( new RoutingLayerPrivate( this, widget ) )
0639 {
0640     connect( widget->model()->routingManager(), SIGNAL(stateChanged(RoutingManager::State)),
0641              this, SLOT(updateRouteState()) );
0642     connect( widget, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)),
0643             this, SLOT(setViewportChanged()) );
0644     connect(widget->model()->routingManager()->alternativeRoutesModel(), SIGNAL(currentRouteChanged(const GeoDataDocument*)),
0645             this, SLOT(setViewportChanged()) );
0646     connect(widget->model()->routingManager()->alternativeRoutesModel(), SIGNAL(currentRouteChanged(const GeoDataDocument*)),
0647              this, SIGNAL(repaintNeeded()) );
0648     connect( widget->model()->routingManager()->alternativeRoutesModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
0649              this, SLOT(showAlternativeRoutes()) );
0650 }
0651 
0652 RoutingLayer::~RoutingLayer()
0653 {
0654     delete d;
0655 }
0656 
0657 QStringList RoutingLayer::renderPosition() const
0658 {
0659     return QStringList(QStringLiteral("HOVERS_ABOVE_SURFACE"));
0660 }
0661 
0662 qreal RoutingLayer::zValue() const
0663 {
0664     return 1.0;
0665 }
0666 
0667 bool RoutingLayer::render( GeoPainter *painter, ViewportParams *viewport,
0668                            const QString& renderPos, GeoSceneLayer *layer )
0669 {
0670     Q_UNUSED( viewport )
0671     Q_UNUSED( renderPos )
0672     Q_UNUSED( layer )
0673 
0674     painter->save();
0675 
0676     if ( d->m_placemarkModel) {
0677         d->renderPlacemarks( painter );
0678     }
0679 
0680     if ( d->m_alternativeRoutesModel ) {
0681         d->renderAlternativeRoutes( painter );
0682     }
0683 
0684     d->renderRoute( painter );
0685 
0686     if ( d->m_routeRequest) {
0687         d->renderRequest( painter );
0688     }
0689 
0690     d->renderAnnotations( painter );
0691 
0692     painter->restore();
0693     if ( d->m_viewportChanged && d->m_viewContext == Still ) {
0694         d->m_viewportChanged = false;
0695     }
0696     return true;
0697 }
0698 
0699 RenderState RoutingLayer::renderState() const
0700 {
0701     return RenderState(QStringLiteral("Routing"), d->m_marbleWidget->model()->routingManager()->state() == RoutingManager::Downloading ? WaitingForUpdate : Complete);
0702 }
0703 
0704 bool RoutingLayer::eventFilter( QObject *obj, QEvent *event )
0705 {
0706     Q_UNUSED( obj )
0707 
0708     if ( !d->m_isInteractive ) {
0709         return false;
0710     }
0711 
0712     if ( event->type() == QEvent::MouseButtonPress ) {
0713         QMouseEvent *e = static_cast<QMouseEvent*>( event );
0714         return d->handleMouseButtonPress( e );
0715     }
0716 
0717     if ( event->type() == QEvent::MouseButtonRelease ) {
0718         QMouseEvent *e = static_cast<QMouseEvent*>( event );
0719         return d->handleMouseButtonRelease( e );
0720     }
0721 
0722     if ( event->type() == QEvent::MouseMove ) {
0723         QMouseEvent *e = static_cast<QMouseEvent*>( event );
0724         return d->handleMouseMove( e );
0725     }
0726 
0727     return false;
0728 }
0729 
0730 void RoutingLayer::setPlacemarkModel ( MarblePlacemarkModel *model )
0731 {
0732     d->m_placemarkModel = model;
0733     setViewportChanged();
0734 }
0735 
0736 void RoutingLayer::synchronizeWith( QItemSelectionModel *selection )
0737 {
0738     d->m_selectionModel = selection;
0739 }
0740 
0741 void RoutingLayer::removeViaPoint()
0742 {
0743     if ( d->m_activeMenuIndex >= 0 ) {
0744         d->m_routeRequest->remove( d->m_activeMenuIndex );
0745         d->m_activeMenuIndex = -1;
0746         emit repaintNeeded();
0747         d->m_marbleWidget->model()->routingManager()->retrieveRoute();
0748     }
0749 }
0750 
0751 void RoutingLayer::showAlternativeRoutes()
0752 {
0753     setViewportChanged();
0754     emit repaintNeeded();
0755 }
0756 
0757 void RoutingLayer::exportRoute()
0758 {
0759     QString fileName = QFileDialog::getSaveFileName( d->m_marbleWidget,
0760                        tr( "Export Route" ), // krazy:exclude=qclasses
0761                        QDir::homePath(),
0762                        tr( "GPX and KML files (*.gpx *.kml)" ) );
0763 
0764     if ( !fileName.isEmpty() ) {
0765         if ( fileName.endsWith( QLatin1String( ".gpx" ), Qt::CaseInsensitive ) ) {
0766             QFile gpx( fileName );
0767             if ( gpx.open( QFile::WriteOnly) ) {
0768                 d->m_routingModel->exportGpx( &gpx );
0769                 gpx.close();
0770             }
0771         } else {
0772             d->m_marbleWidget->model()->routingManager()->saveRoute( fileName );
0773         }
0774     }
0775 }
0776 
0777 void RoutingLayer::updateRouteState()
0778 {
0779     setViewportChanged();
0780     emit repaintNeeded();
0781 }
0782 
0783 void RoutingLayer::setViewportChanged()
0784 {
0785     d->m_viewportChanged = true;
0786     d->m_routeRegion = QRegion();
0787     d->m_instructionRegions.clear();
0788     d->m_alternativeRouteRegions.clear();
0789 }
0790 
0791 void RoutingLayer::setViewContext( ViewContext viewContext )
0792 {
0793     d->m_viewContext = viewContext;
0794 }
0795 
0796 void RoutingLayer::setInteractive( bool interactive )
0797 {
0798     d->m_isInteractive = interactive;
0799 }
0800 
0801 bool RoutingLayer::isInteractive() const
0802 {
0803     return d->m_isInteractive;
0804 }
0805 
0806 QString RoutingLayer::runtimeTrace() const
0807 {
0808     return QStringLiteral("Routing Layer");
0809 }
0810 
0811 } // namespace Marble
0812 
0813 #include "moc_RoutingLayer.cpp"