|
21 | 21 | #include "qgsnetworkaccessmanager.h"
|
22 | 22 | #include "qgsmessagelog.h"
|
23 | 23 | #include "qgssymbollayerutils.h"
|
| 24 | +#include "qgsnetworkcontentfetchertask.h" |
24 | 25 |
|
25 | 26 | #include <QApplication>
|
26 | 27 | #include <QCoreApplication>
|
@@ -80,8 +81,10 @@ int QgsSvgCacheEntry::dataSize() const
|
80 | 81 |
|
81 | 82 | QgsSvgCache::QgsSvgCache( QObject *parent )
|
82 | 83 | : QObject( parent )
|
| 84 | + , mMutex( QMutex::Recursive ) |
83 | 85 | {
|
84 | 86 | mMissingSvg = QStringLiteral( "<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).toLatin1();
|
| 87 | + mFetchingSvg = QStringLiteral( "<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>x</text></svg>" ).toLatin1(); |
85 | 88 | }
|
86 | 89 |
|
87 | 90 | QgsSvgCache::~QgsSvgCache()
|
@@ -406,77 +409,70 @@ QByteArray QgsSvgCache::getImageData( const QString &path ) const
|
406 | 409 | return mMissingSvg;
|
407 | 410 | }
|
408 | 411 |
|
409 |
| - // the url points to a remote resource, download it! |
410 |
| - QNetworkReply *reply = nullptr; |
| 412 | + QMutexLocker locker( &mMutex ); |
| 413 | + |
| 414 | + // already a request in progress for this url |
| 415 | + if ( mPendingRemoteUrls.contains( path ) ) |
| 416 | + return mFetchingSvg; |
411 | 417 |
|
412 |
| - // The following code blocks until the file is downloaded... |
413 |
| - // TODO: use signals to get reply finished notification, in this moment |
414 |
| - // it's executed while rendering. |
415 |
| - while ( true ) |
| 418 | + if ( mRemoteContentCache.contains( path ) ) |
416 | 419 | {
|
417 |
| - QgsDebugMsg( QString( "get svg: %1" ).arg( svgUrl.toString() ) ); |
418 |
| - QNetworkRequest request( svgUrl ); |
419 |
| - request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache ); |
420 |
| - request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true ); |
| 420 | + // already fetched this content - phew. Just return what we already got. |
| 421 | + return *mRemoteContentCache[ path ]; |
| 422 | + } |
421 | 423 |
|
422 |
| - reply = QgsNetworkAccessManager::instance()->get( request ); |
423 |
| - connect( reply, &QNetworkReply::downloadProgress, this, &QgsSvgCache::downloadProgress ); |
| 424 | + mPendingRemoteUrls.insert( path ); |
| 425 | + //fire up task to fetch image in background |
| 426 | + QNetworkRequest request( svgUrl ); |
| 427 | + request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache ); |
| 428 | + request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true ); |
424 | 429 |
|
425 |
| - //emit statusChanged( tr( "Downloading svg." ) ); |
| 430 | + QgsNetworkContentFetcherTask *task = new QgsNetworkContentFetcherTask( request ); |
| 431 | + connect( task, &QgsNetworkContentFetcherTask::fetched, this, [this, task, path] |
| 432 | + { |
| 433 | + QMutexLocker locker( &mMutex ); |
426 | 434 |
|
427 |
| - // wait until the image download finished |
428 |
| - // TODO: connect to the reply->finished() signal |
429 |
| - while ( !reply->isFinished() ) |
| 435 | + QNetworkReply *reply = task->reply(); |
| 436 | + if ( !reply ) |
430 | 437 | {
|
431 |
| - QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents, 500 ); |
| 438 | + // cancelled |
| 439 | + QMetaObject::invokeMethod( const_cast< QgsSvgCache * >( this ), "onRemoteSvgFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG( bool, false ) ); |
| 440 | + return; |
432 | 441 | }
|
433 | 442 |
|
434 | 443 | if ( reply->error() != QNetworkReply::NoError )
|
435 | 444 | {
|
436 | 445 | QgsMessageLog::logMessage( tr( "SVG request failed [error: %1 - url: %2]" ).arg( reply->errorString(), path ), tr( "SVG" ) );
|
437 |
| - reply->deleteLater(); |
438 |
| - return mMissingSvg; |
| 446 | + return; |
439 | 447 | }
|
440 | 448 |
|
441 |
| - QVariant redirect = reply->attribute( QNetworkRequest::RedirectionTargetAttribute ); |
442 |
| - if ( redirect.isNull() ) |
| 449 | + QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ); |
| 450 | + if ( !status.isNull() && status.toInt() >= 400 ) |
443 | 451 | {
|
444 |
| - // neither network error nor redirection |
445 |
| - // TODO: cache the image |
446 |
| - break; |
| 452 | + QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ); |
| 453 | + QgsMessageLog::logMessage( tr( "SVG request error [status: %1 - reason phrase: %2] for %3" ).arg( status.toInt() ).arg( phrase.toString(), path ), tr( "SVG" ) ); |
| 454 | + mRemoteContentCache.insert( path, new QByteArray( mMissingSvg ) ); |
| 455 | + return; |
447 | 456 | }
|
448 | 457 |
|
449 |
| - // do a new request to the redirect url |
450 |
| - svgUrl = redirect.toUrl(); |
451 |
| - reply->deleteLater(); |
452 |
| - } |
453 |
| - |
454 |
| - QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ); |
455 |
| - if ( !status.isNull() && status.toInt() >= 400 ) |
456 |
| - { |
457 |
| - QVariant phrase = reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ); |
458 |
| - QgsMessageLog::logMessage( tr( "SVG request error [status: %1 - reason phrase: %2] for %3" ).arg( status.toInt() ).arg( phrase.toString(), path ), tr( "SVG" ) ); |
459 |
| - |
460 |
| - reply->deleteLater(); |
461 |
| - return mMissingSvg; |
462 |
| - } |
463 |
| - |
464 |
| - // we accept both real SVG mime types AND plain text types - because some sites |
465 |
| - // (notably github) serve up svgs as raw text |
466 |
| - QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString(); |
467 |
| - if ( !contentType.startsWith( QLatin1String( "image/svg+xml" ), Qt::CaseInsensitive ) |
468 |
| - && !contentType.startsWith( QLatin1String( "text/plain" ), Qt::CaseInsensitive ) ) |
469 |
| - { |
470 |
| - QgsMessageLog::logMessage( tr( "Unexpected MIME type %1 received for %2" ).arg( contentType, path ), tr( "SVG" ) ); |
471 |
| - reply->deleteLater(); |
472 |
| - return mMissingSvg; |
473 |
| - } |
| 458 | + // we accept both real SVG mime types AND plain text types - because some sites |
| 459 | + // (notably github) serve up svgs as raw text |
| 460 | + QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString(); |
| 461 | + if ( !contentType.startsWith( QLatin1String( "image/svg+xml" ), Qt::CaseInsensitive ) |
| 462 | + && !contentType.startsWith( QLatin1String( "text/plain" ), Qt::CaseInsensitive ) ) |
| 463 | + { |
| 464 | + QgsMessageLog::logMessage( tr( "Unexpected MIME type %1 received for %2" ).arg( contentType, path ), tr( "SVG" ) ); |
| 465 | + mRemoteContentCache.insert( path, new QByteArray( mMissingSvg ) ); |
| 466 | + return; |
| 467 | + } |
474 | 468 |
|
475 |
| - // read the image data |
476 |
| - QByteArray ba = reply->readAll(); |
477 |
| - reply->deleteLater(); |
| 469 | + // read the image data |
| 470 | + mRemoteContentCache.insert( path, new QByteArray( reply->readAll() ) ); |
| 471 | + QMetaObject::invokeMethod( const_cast< QgsSvgCache * >( this ), "onRemoteSvgFetched", Qt::QueuedConnection, Q_ARG( QString, path ), Q_ARG( bool, true ) ); |
| 472 | + } ); |
478 | 473 |
|
479 |
| - return ba; |
| 474 | + QgsApplication::taskManager()->addTask( task ); |
| 475 | + return mFetchingSvg; |
480 | 476 | }
|
481 | 477 |
|
482 | 478 | void QgsSvgCache::cacheImage( QgsSvgCacheEntry *entry )
|
@@ -998,3 +994,25 @@ void QgsSvgCache::downloadProgress( qint64 bytesReceived, qint64 bytesTotal )
|
998 | 994 | QgsDebugMsg( msg );
|
999 | 995 | emit statusChanged( msg );
|
1000 | 996 | }
|
| 997 | + |
| 998 | +void QgsSvgCache::onRemoteSvgFetched( const QString &url, bool success ) |
| 999 | +{ |
| 1000 | + QMutexLocker locker( &mMutex ); |
| 1001 | + mPendingRemoteUrls.remove( url ); |
| 1002 | + |
| 1003 | + QgsSvgCacheEntry *nextEntry = mLeastRecentEntry; |
| 1004 | + while ( QgsSvgCacheEntry *entry = nextEntry ) |
| 1005 | + { |
| 1006 | + nextEntry = entry->nextEntry; |
| 1007 | + if ( entry->path == url ) |
| 1008 | + { |
| 1009 | + takeEntryFromList( entry ); |
| 1010 | + mEntryLookup.remove( entry->path, entry ); |
| 1011 | + mTotalSize -= entry->dataSize(); |
| 1012 | + delete entry; |
| 1013 | + } |
| 1014 | + } |
| 1015 | + |
| 1016 | + if ( success ) |
| 1017 | + emit remoteSvgFetched( url ); |
| 1018 | +} |
0 commit comments