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 ®ion_ ) : 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 ®ion: 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 ®ion: 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 ®ion: 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 ®ion: 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 ®ion: m_regions ) { 0595 if ( region.region.contains( point ) ) { 0596 return true; 0597 } 0598 } 0599 0600 for( const ModelRegion ®ion: 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 ®ion: 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"