33 #include <QSortFilterProxyModel>
36 #include <QFileDialog>
38 #include <QMouseEvent>
40 #include <QToolButton>
41 #include <QProgressDialog>
43 #include "ui_RoutingWidget.h"
48 class RoutingWidgetPrivate
51 Ui::RoutingWidget m_ui;
55 RoutingManager *
const m_routingManager;
57 RoutingLayer *
const m_routingLayer;
59 RoutingInputWidget *m_activeInput;
63 RoutingInputWidget *m_inputRequest;
67 RouteRequest *
const m_routeRequest;
69 RouteSyncManager *m_routeSyncManager;
71 bool m_zoomRouteAfterDownload;
89 QAction *m_openCloudRoutesAction;
98 RoutingWidgetPrivate(RoutingWidget *parent,
MarbleWidget *marbleWidget );
104 void adjustInputWidgets();
106 void adjustSearchButton();
114 void setActiveInput( RoutingInputWidget* widget );
119 void createProgressAnimation();
120 RoutingWidget *m_parent;
123 RoutingWidgetPrivate::RoutingWidgetPrivate( RoutingWidget *parent,
MarbleWidget *marbleWidget ) :
124 m_widget( marbleWidget ),
125 m_routingManager( marbleWidget->model()->routingManager() ),
126 m_routingLayer( marbleWidget->routingLayer() ),
129 m_routingModel( m_routingManager->routingModel() ),
130 m_routeRequest( marbleWidget->model()->routingManager()->routeRequest() ),
131 m_routeSyncManager( 0 ),
132 m_zoomRouteAfterDownload( false ),
135 m_collapse_width( 0 ),
137 m_openRouteButton( 0 ),
138 m_saveRouteButton( 0 ),
139 m_cloudSyncSeparator( 0 ),
140 m_uploadToCloudAction( 0 ),
141 m_openCloudRoutesAction( 0 ),
143 m_reverseRouteButton( 0 ),
144 m_clearRouteButton( 0 ),
145 m_configureButton( 0 ),
146 m_routeUploadDialog( 0 ),
149 createProgressAnimation();
150 m_progressTimer.setInterval( 100 );
156 void RoutingWidgetPrivate::adjustInputWidgets()
158 for (
int i = 0; i < m_inputWidgets.size(); ++i ) {
159 m_inputWidgets[i]->setIndex( i );
162 adjustSearchButton();
165 void RoutingWidgetPrivate::adjustSearchButton()
168 QString tooltip =
QObject::tr(
"Retrieve routing instructions for the selected destinations." );
171 for (
int i = 0; i < m_inputWidgets.size(); ++i ) {
172 if ( m_inputWidgets[i]->hasTargetPosition() ) {
177 if ( validInputs < 2 ) {
179 tooltip =
QObject::tr(
"Find places matching the search term" );
182 m_ui.searchButton->setText( text );
183 m_ui.searchButton->setToolTip( tooltip );
186 void RoutingWidgetPrivate::setActiveInput( RoutingInputWidget *widget )
188 Q_ASSERT( widget &&
"Must not pass null" );
189 MarblePlacemarkModel *model = widget->searchResultModel();
191 m_activeInput = widget;
192 m_ui.directionsListView->setModel( model );
193 m_routingLayer->setPlacemarkModel( model );
194 m_routingLayer->synchronizeWith( m_ui.directionsListView->selectionModel() );
197 void RoutingWidgetPrivate::setupToolBar()
203 m_openRouteButton->setIcon(
QIcon(
":/icons/16x16/document-open.png") );
204 m_toolBar->addWidget(m_openRouteButton);
208 m_saveRouteButton->setIcon(
QIcon(
":/icons/16x16/document-save.png") );
209 m_toolBar->addWidget(m_saveRouteButton);
211 m_cloudSyncSeparator = m_toolBar->addSeparator();
212 m_uploadToCloudAction = m_toolBar->addAction(
QObject::tr(
"Upload to Cloud") );
213 m_uploadToCloudAction->setToolTip(
QObject::tr(
"Upload to Cloud") );
214 m_uploadToCloudAction->setIcon(
QIcon(
":/icons/cloud-upload.png") );
216 m_openCloudRoutesAction = m_toolBar->addAction(
QObject::tr(
"Manage Cloud Routes") );
217 m_openCloudRoutesAction->setToolTip(
QObject::tr(
"Manage Cloud Routes") );
218 m_openCloudRoutesAction->setIcon(
QIcon(
":/icons/cloud-download.png") );
220 m_toolBar->addSeparator();
223 m_addViaButton->setIcon(
QIcon(
":/marble/list-add.png") );
224 m_toolBar->addWidget(m_addViaButton);
228 m_reverseRouteButton->setIcon(
QIcon(
":/marble/reverse.png") );
229 m_toolBar->addWidget(m_reverseRouteButton);
233 m_clearRouteButton->setIcon(
QIcon(
":/marble/edit-clear.png") );
234 m_toolBar->addWidget(m_clearRouteButton);
236 m_toolBar->addSeparator();
240 m_configureButton->setIcon(
QIcon(
":/icons/16x16/configure.png") );
241 m_toolBar->addWidget(m_configureButton);
244 m_parent, SLOT(openRoute()) );
246 m_parent, SLOT(saveRoute()) );
248 m_parent, SLOT(uploadToCloud()) );
250 m_parent, SLOT(openCloudRoutesDialog()));
252 m_parent, SLOT(addInputWidget()) );
254 m_routingManager, SLOT(reverseRoute()) );
256 m_routingManager, SLOT(clearRoute()) );
258 m_parent, SLOT(configureProfile()) );
260 m_toolBar->setIconSize(
QSize(16, 16));
261 m_ui.toolBarLayout->addWidget(m_toolBar, 0, Qt::AlignLeft);
264 void RoutingWidgetPrivate::createProgressAnimation()
267 qreal
const h = m_iconSize / 2.0;
268 qreal
const q = h / 2.0;
270 qreal
const r = d / 2.0;
273 QImage canvas( m_iconSize, m_iconSize, QImage::Format_ARGB32 );
275 painter.setRenderHint( QPainter::Antialiasing,
true );
276 painter.setPen(
QColor ( Qt::gray ) );
277 painter.setBrush(
QColor( Qt::white ) );
280 for(
double t = 0.0; t < 2 *
M_PI; t += M_PI / 8.0 ) {
281 canvas.fill( Qt::transparent );
282 QRectF firstCircle( h - r + q * cos( t ), h - r + q * sin( t ), d, d );
283 QRectF secondCircle( h - r + q * cos( t + M_PI ), h - r + q * sin( t + M_PI ), d, d );
284 painter.drawEllipse( firstCircle );
285 painter.drawEllipse( secondCircle );
291 QWidget( parent ), d( new RoutingWidgetPrivate( this, marbleWidget ) )
295 d->m_ui.routeComboBox->setVisible(
false );
296 d->m_ui.routeComboBox->setModel( d->m_routingManager->alternativeRoutesModel() );
299 d->m_ui.routingProfileComboBox->setModel( d->m_routingManager->profilesModel() );
301 connect( d->m_routingManager->profilesModel(), SIGNAL(rowsInserted(
QModelIndex,
int,
int)),
302 this, SLOT(selectFirstProfile()) );
303 connect( d->m_routingManager->profilesModel(), SIGNAL(modelReset()),
304 this, SLOT(selectFirstProfile()) );
311 connect( d->m_routeRequest, SIGNAL(positionAdded(
int)),
312 this, SLOT(insertInputWidget(
int)) );
313 connect( d->m_routeRequest, SIGNAL(positionRemoved(
int)),
314 this, SLOT(removeInputWidget(
int)) );
315 connect( d->m_routeRequest, SIGNAL(routingProfileChanged()),
316 this, SLOT(updateActiveRoutingProfile()) );
317 connect( &d->m_progressTimer, SIGNAL(timeout()),
318 this, SLOT(updateProgress()) );
319 connect( d->m_ui.routeComboBox, SIGNAL(currentIndexChanged(
int)),
320 d->m_routingManager->alternativeRoutesModel(), SLOT(setCurrentRoute(
int)) );
321 connect( d->m_routingManager->alternativeRoutesModel(), SIGNAL(currentRouteChanged(
int)),
322 d->m_ui.routeComboBox, SLOT(setCurrentIndex(
int)) );
323 connect( d->m_ui.routingProfileComboBox, SIGNAL(currentIndexChanged(
int)),
324 this, SLOT(setRoutingProfile(
int)) );
325 connect( d->m_ui.routingProfileComboBox, SIGNAL(activated(
int)),
326 this, SLOT(retrieveRoute()) );
327 connect( d->m_routingManager->alternativeRoutesModel(), SIGNAL(rowsInserted(
QModelIndex,
int,
int)),
328 this, SLOT(updateAlternativeRoutes()) );
330 d->m_ui.directionsListView->setModel( d->m_routingModel );
333 d->m_routingLayer->synchronizeWith( selectionModel );
338 connect( d->m_ui.searchButton, SIGNAL(clicked()),
339 this, SLOT(retrieveRoute()) );
340 connect( d->m_ui.showInstructionsButton, SIGNAL(clicked(
bool)),
341 this, SLOT(showDirections()) );
343 for(
int i=0; i<d->m_routeRequest->size(); ++i ) {
344 insertInputWidget( i );
347 for (
int i=0; i<2 && d->m_inputWidgets.size()<2; ++i ) {
352 d->m_ui.resultLabel->setVisible(
false );
354 updateActiveRoutingProfile();
355 updateCloudSyncButtons();
358 d->m_ui.directionsListView->setVisible(
false );
359 d->m_openRouteButton->setVisible(
false );
360 d->m_saveRouteButton->setVisible(
false );
362 d->m_ui.directionsListView->setAttribute( Qt::WA_Maemo5StackedWindow );
363 d->m_ui.directionsListView->setWindowFlags( Qt::Window );
364 d->m_ui.directionsListView->setWindowTitle(
tr(
"Directions - Marble" ) );
365 #endif // Q_WS_MAEMO_5
374 void RoutingWidget::retrieveRoute()
376 if ( d->m_inputWidgets.size() == 1 ) {
378 d->m_inputWidgets.first()->findPlacemarks();
382 int index = d->m_ui.routingProfileComboBox->currentIndex();
386 d->m_routeRequest->setRoutingProfile( d->m_routingManager->profilesModel()->profiles().at( index ) );
388 Q_ASSERT( d->m_routeRequest->size() == d->m_inputWidgets.size() );
389 for (
int i = 0; i < d->m_inputWidgets.size(); ++i ) {
390 RoutingInputWidget *widget = d->m_inputWidgets.at( i );
391 if ( !widget->hasTargetPosition() && widget->hasInput() ) {
392 widget->findPlacemarks();
397 d->m_activeInput = 0;
398 if ( d->m_routeRequest->size() > 1 ) {
399 d->m_zoomRouteAfterDownload =
true;
400 d->m_routingLayer->setPlacemarkModel( 0 );
401 d->m_routingManager->retrieveRoute();
402 d->m_ui.directionsListView->setModel( d->m_routingModel );
403 d->m_routingLayer->synchronizeWith( d->m_ui.directionsListView->selectionModel() );
407 void RoutingWidget::activateItem (
const QModelIndex &index )
412 GeoDataCoordinates position = qvariant_cast<GeoDataCoordinates>( data );
413 d->m_widget->centerOn( position,
true );
416 if ( d->m_activeInput && index.
isValid() ) {
419 d->m_activeInput->setTargetPosition( data.
value<GeoDataCoordinates>(), index.
data().
toString() );
424 void RoutingWidget::handleSearchResult( RoutingInputWidget *widget )
426 d->setActiveInput( widget );
427 MarblePlacemarkModel *model = widget->searchResultModel();
429 if ( model->rowCount() ) {
430 QString const results =
tr(
"placemarks found: %1" ).
arg( model->rowCount() );
431 d->m_ui.resultLabel->setText( results );
432 d->m_ui.resultLabel->setVisible(
true );
434 activatePlacemark( model->index( 0, 0 ) );
436 QString const results =
tr(
"No placemark found" );
437 d->m_ui.resultLabel->setText(
"<font color=\"red\">" + results +
"</font>" );
438 d->m_ui.resultLabel->setVisible(
true );
441 GeoDataLineString placemarks;
442 for (
int i = 0; i < model->rowCount(); ++i ) {
445 placemarks << data.
value<GeoDataCoordinates>();
449 if ( placemarks.size() > 1 ) {
454 d->m_ui.directionsListView->setVisible(
true );
459 void RoutingWidget::centerOnInputWidget( RoutingInputWidget *widget )
461 if ( widget->hasTargetPosition() ) {
462 d->m_widget->centerOn( widget->targetPosition() );
466 void RoutingWidget::activatePlacemark(
const QModelIndex &index )
468 if ( d->m_activeInput && index.
isValid() ) {
471 d->m_activeInput->setTargetPosition( data.
value<GeoDataCoordinates>() );
475 d->m_ui.directionsListView->setCurrentIndex( index );
483 void RoutingWidget::insertInputWidget(
int index )
485 if ( index >= 0 && index <= d->m_inputWidgets.size() ) {
487 d->m_inputWidgets.insert( index, input );
496 connect( input, SIGNAL(targetValidityChanged(
bool)),
497 this, SLOT(adjustSearchButton()) );
499 d->m_ui.routingLayout->insertWidget( index, input );
500 d->adjustInputWidgets();
504 void RoutingWidget::removeInputWidget( RoutingInputWidget *widget )
506 int index = d->m_inputWidgets.indexOf( widget );
508 if ( d->m_inputWidgets.size() < 3 ) {
511 d->m_routeRequest->remove( index );
513 d->m_routingManager->retrieveRoute();
517 void RoutingWidget::removeInputWidget(
int index )
519 if ( index >= 0 && index < d->m_inputWidgets.size() ) {
520 RoutingInputWidget *widget = d->m_inputWidgets.at( index );
521 d->m_inputWidgets.remove( index );
522 d->m_ui.routingLayout->removeWidget( widget );
523 widget->deleteLater();
524 if ( widget == d->m_activeInput ) {
525 d->m_activeInput = 0;
526 d->m_routingLayer->setPlacemarkModel( 0 );
527 d->m_ui.directionsListView->setModel( d->m_routingModel );
528 d->m_routingLayer->synchronizeWith( d->m_ui.directionsListView->selectionModel() );
530 d->adjustInputWidgets();
533 if ( d->m_inputWidgets.size() < 2 ) {
541 d->m_ui.routeComboBox->setVisible(
false );
542 d->m_ui.routeComboBox->clear();
546 d->m_progressTimer.start();
549 d->m_saveRouteButton->setEnabled( d->m_routingManager->routingModel()->rowCount() > 0 );
552 void RoutingWidget::requestMapPosition( RoutingInputWidget *widget,
bool enabled )
554 pointSelectionCanceled();
557 d->m_inputRequest = widget;
558 d->m_widget->installEventFilter(
this );
559 d->m_widget->setFocus( Qt::OtherFocusReason );
563 void RoutingWidget::retrieveSelectedPoint(
const GeoDataCoordinates &coordinates )
565 if ( d->m_inputRequest && d->m_inputWidgets.contains( d->m_inputRequest ) ) {
566 d->m_inputRequest->setTargetPosition( coordinates );
567 d->m_widget->update();
570 d->m_inputRequest = 0;
571 d->m_widget->removeEventFilter(
this );
574 void RoutingWidget::adjustSearchButton()
576 d->adjustSearchButton();
579 void RoutingWidget::pointSelectionCanceled()
581 if ( d->m_inputRequest && d->m_inputWidgets.contains( d->m_inputRequest ) ) {
582 d->m_inputRequest->abortMapInputRequest();
585 d->m_inputRequest = 0;
586 d->m_widget->removeEventFilter(
this );
589 void RoutingWidget::configureProfile()
591 int index = d->m_ui.routingProfileComboBox->currentIndex();
593 RoutingProfileSettingsDialog dialog( d->m_widget->model()->pluginManager(), d->m_routingManager->profilesModel(), this );
594 dialog.editProfile( d->m_ui.routingProfileComboBox->currentIndex() );
595 d->m_routeRequest->setRoutingProfile( d->m_routingManager->profilesModel()->profiles().at( index ) );
599 void RoutingWidget::updateProgress()
601 if ( !d->m_progressAnimation.isEmpty() ) {
602 d->m_currentFrame = ( d->m_currentFrame + 1 ) % d->m_progressAnimation.size();
603 QIcon frame = d->m_progressAnimation[d->m_currentFrame];
604 d->m_ui.searchButton->setIcon( frame );
605 d->m_ui.resultLabel->setVisible(
false );
609 void RoutingWidget::updateAlternativeRoutes()
611 if ( d->m_ui.routeComboBox->count() == 1) {
613 GeoDataLatLonBox
const bbox = d->m_routingManager->routingModel()->route().bounds();
614 if ( d->m_zoomRouteAfterDownload ) {
615 d->m_zoomRouteAfterDownload =
false;
616 d->m_widget->centerOn( bbox );
620 d->m_ui.routeComboBox->setVisible( d->m_ui.routeComboBox->count() > 0 );
621 if ( d->m_ui.routeComboBox->currentIndex() < 0 && d->m_ui.routeComboBox->count() > 0 ) {
622 d->m_ui.routeComboBox->setCurrentIndex( 0 );
625 d->m_progressTimer.stop();
626 d->m_ui.searchButton->setIcon(
QIcon() );
628 QString const results =
tr(
"routes found: %1" ).
arg( d->m_ui.routeComboBox->count() );
629 d->m_ui.resultLabel->setText( results );
630 d->m_ui.resultLabel->setVisible(
true );
631 d->m_saveRouteButton->setEnabled( d->m_routingManager->routingModel()->rowCount() > 0 );
636 d->m_ui.showInstructionsButton->setVisible( visible );
641 d->m_routeSyncManager = manager;
642 connect( d->m_routeSyncManager, SIGNAL(routeSyncEnabledChanged(
bool)),
643 this, SLOT(updateCloudSyncButtons()) );
644 updateCloudSyncButtons();
650 d->m_routingManager->lastOpenPath(),
tr(
"KML Files (*.kml)") );
652 d->m_routingManager->setLastOpenPath(
QFileInfo( file ).absolutePath() );
653 d->m_zoomRouteAfterDownload =
true;
654 d->m_routingManager->loadRoute( file );
655 updateAlternativeRoutes();
659 void RoutingWidget::selectFirstProfile()
661 int count = d->m_routingManager->profilesModel()->rowCount();
662 if ( count && d->m_ui.routingProfileComboBox->currentIndex() < 0 ) {
663 d->m_ui.routingProfileComboBox->setCurrentIndex( 0 );
667 void RoutingWidget::setRoutingProfile(
int index )
669 if ( index >= 0 && index < d->m_routingManager->profilesModel()->rowCount() ) {
670 d->m_routeRequest->setRoutingProfile( d->m_routingManager->profilesModel()->profiles().at( index ) );
674 void RoutingWidget::showDirections()
676 d->m_ui.directionsListView->setVisible(
true );
683 d->m_routingManager->lastSavePath(),
684 tr(
"KML files (*.kml)" ) );
689 fileName.
append(
".kml" );
691 d->m_routingManager->setLastSavePath(
QFileInfo( fileName ).absolutePath() );
692 d->m_routingManager->saveRoute( fileName );
698 Q_ASSERT( d->m_routeSyncManager );
700 if (!d->m_routeUploadDialog) {
702 d->m_routeUploadDialog->setWindowTitle(
tr(
"Uploading route..." ) );
703 d->m_routeUploadDialog->setMinimum( 0 );
704 d->m_routeUploadDialog->setMaximum( 100 );
705 d->m_routeUploadDialog->setAutoClose(
true );
706 d->m_routeUploadDialog->setAutoReset(
true );
707 connect( d->m_routeSyncManager, SIGNAL(routeUploadProgress(qint64,qint64)),
this, SLOT(updateUploadProgress(qint64,qint64)) );
710 d->m_routeUploadDialog->show();
711 d->m_routeSyncManager->uploadRoute();
716 Q_ASSERT( d->m_routeSyncManager );
717 d->m_routeSyncManager->prepareRouteList();
720 connect( d->m_routeSyncManager, SIGNAL(routeListDownloadProgress(qint64,qint64)), dialog, SLOT(updateListDownloadProgressbar(qint64,qint64)) );
721 connect( dialog, SIGNAL(downloadButtonClicked(
QString)), d->m_routeSyncManager, SLOT(downloadRoute(
QString)) );
723 connect( dialog, SIGNAL(deleteButtonClicked(
QString)), d->m_routeSyncManager, SLOT(deleteRoute(
QString)) );
724 connect( dialog, SIGNAL(removeFromCacheButtonClicked(
QString)), d->m_routeSyncManager, SLOT(removeRouteFromCache(
QString)) );
725 connect( dialog, SIGNAL(uploadToCloudButtonClicked(
QString)), d->m_routeSyncManager, SLOT(uploadRoute(
QString)) );
733 d->m_progressTimer.stop();
734 d->m_ui.searchButton->setIcon(
QIcon() );
735 QString const results =
tr(
"No route found" );
736 d->m_ui.resultLabel->setText(
"<font color=\"red\">" + results +
"</font>" );
737 d->m_ui.resultLabel->setVisible(
true );
741 void RoutingWidget::updateActiveRoutingProfile()
743 RoutingProfile
const profile = d->m_routingManager->routeRequest()->routingProfile();
745 d->m_ui.routingProfileComboBox->setCurrentIndex( profiles.
indexOf( profile ) );
748 void RoutingWidget::updateCloudSyncButtons()
750 bool const show = d->m_routeSyncManager && d->m_routeSyncManager->isRouteSyncEnabled();
751 d->m_cloudSyncSeparator->setVisible( show );
752 d->m_uploadToCloudAction->setVisible( show );
753 d->m_openCloudRoutesAction->setVisible( show );
756 void RoutingWidget::openCloudRoute(
const QString &identifier)
758 Q_ASSERT( d->m_routeSyncManager );
759 d->m_routeSyncManager->openRoute( identifier );
760 d->m_widget->centerOn( d->m_routingManager->routingModel()->route().bounds() );
763 void RoutingWidget::updateUploadProgress(qint64 sent, qint64 total)
765 Q_ASSERT( d->m_routeUploadDialog );
766 d->m_routeUploadDialog->setValue( 100.0 * sent / total );
771 if ( o != d->m_widget ) {
775 Q_ASSERT( d->m_inputRequest != 0 );
776 Q_ASSERT( d->m_inputWidgets.contains( d->m_inputRequest ) );
778 if ( event->
type() == QEvent::MouseButtonPress ) {
780 return e->
button() == Qt::LeftButton;
783 if ( event->
type() == QEvent::MouseButtonRelease ) {
785 qreal lon( 0.0 ), lat( 0.0 );
786 if ( e->
button() == Qt::LeftButton && d->m_widget->geoCoordinates( e->
pos().
x(), e->
pos().
y(),
795 if ( event->
type() == QEvent::MouseMove ) {
796 d->m_widget->setCursor( Qt::CrossCursor );
800 if ( event->
type() == QEvent::KeyPress ) {
802 if ( e->
key() == Qt::Key_Escape ) {
803 pointSelectionCanceled();
820 #include "RoutingWidget.moc"
A 3d point representation.
QString & append(QChar ch)
A container for Features, Styles and in the future Schemas.
This file contains the headers for MarbleModel.
QPixmap fromImage(const QImage &image, QFlags< Qt::ImageConversionFlag > flags)
QString tr(const char *sourceText, const char *disambiguation, int n)
int indexOf(const T &value, int from) const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
virtual bool eventFilter(QObject *watched, QEvent *event)
static GeoDataLatLonBox fromLineString(const GeoDataLineString &lineString)
Create the smallest bounding box from a line string.
void setMargin(int margin)
static MarbleGlobal * getInstance()
The GeoDataCoordinates coordinate.
QVariant data(int role) const
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFlags< QFileDialog::Option > options)
const QPoint & pos() const
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFlags< QFileDialog::Option > options)
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const