diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b276c02a..ff7ac80a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,353 +1,354 @@ cmake_minimum_required(VERSION 2.8.12) project(marble) #################################################### # CMake Settings SET(CMAKE_COLOR_MAKEFILE ON) # SET(CMAKE_SKIP_RPATH ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) # Let CMake find the correct library dir instead of # relying on the obsolete LIB_SUFFIX parameter include(GNUInstallDirs) # Taken from KDECompilerSettings.cmake: # Pick sensible versions of the C and C++ standards. # Note that MSVC does not have equivalent flags; the features are either # supported or they are not. if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") # We use the C89 standard because that is what is common to all our # compilers (in particular, MSVC 2010 does not support C99) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=iso9899:1990") endif() if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel" AND NOT WIN32) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") endif() # Default to hidden visibility for symbols set(CMAKE_C_VISIBILITY_PRESET hidden) set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) if (POLICY CMP0063) cmake_policy(SET CMP0063 NEW) endif() # RPATH/RUNPATH settings if (UNIX) # Add CMAKE_INSTALL_FULL_LIBDIR to the RPATH to be used when installing, # but only if it isn't a standard system directory. list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_FULL_LIBDIR}" isSystemLibDir) list(FIND CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_FULL_LIBDIR}" isSystemCxxLibDir) list(FIND CMAKE_C_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_FULL_LIBDIR}" isSystemCLibDir) if("${isSystemLibDir}" STREQUAL "-1" AND "${isSystemCxxLibDir}" STREQUAL "-1" AND "${isSystemCLibDir}" STREQUAL "-1") set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_FULL_LIBDIR}") endif() # Add directories which are in the linker search path (but outside the project) # to the RPATH to be used when installing set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # no libs or plugins are designed to be used from the build dir, so directly link with install rpath set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) endif (UNIX) #################################################### # Where to look first for cmake modules, # before ${CMAKE_ROOT}/Modules/ is checked set ( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/cmake_find_rules ${CMAKE_CURRENT_SOURCE_DIR}/cmake_scripts ${CMAKE_MODULE_PATH} ) # add cmake macros include(GenerateExportHeader) include( MarbleMacros ) #################################################### # Generate the tiles with the tilecreator at compile time # if this option is set, srtm.jpg will not be installed but the generated tiles instead option(MOBILE "Create a Marble version optimized for handheld devices") #################################################### # Build a D-Bus interface for the Marble widget # This is disabled by default for all win32, apple and Android if(WIN32 OR APPLE OR CMAKE_SYSTEM_NAME STREQUAL Android) option(BUILD_WITH_DBUS "Build the D-Bus interface for the Marble widget" OFF) else() option(BUILD_WITH_DBUS "Build the D-Bus interface for the Marble widget" ON) endif() ####################################################### # Specific options for building for different platforms if(CMAKE_SYSTEM_NAME STREQUAL Android) add_definitions(-DANDROID) endif() ####################################################### # Find Qt dependencies if(CMAKE_SYSTEM_NAME STREQUAL Android) set(REQUIRED_QT_VERSION 5.7.0) # TODO: still needed with ECM toolchain? SET(QT_QMAKE_EXECUTABLE "$ENV{Qt5_android}/bin/qmake") else() set(REQUIRED_QT_VERSION 5.3.0) endif() find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS Core Xml Network Test Widgets Svg Sql Concurrent Quick PrintSupport ) if(CMAKE_SYSTEM_NAME STREQUAL Android) find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS Positioning Multimedia ) set ( MARBLE_NO_WEBKITWIDGETS TRUE ) else() find_package(Qt5 ${REQUIRED_QT_VERSION} COMPONENTS WebKit WebKitWidgets ) if ( NOT Qt5WebKitWidgets_FOUND ) set ( MARBLE_NO_WEBKITWIDGETS TRUE ) endif() endif() if (BUILD_WITH_DBUS) find_package(Qt5 ${REQUIRED_QT_VERSION} COMPONENTS DBus) if (NOT Qt5DBus_FOUND) set(MARBLE_NO_DBUS TRUE) endif() else() set(MARBLE_NO_DBUS TRUE) endif() SET(CMAKE_AUTOMOC TRUE) # Use M_PI under Windows if( WIN32 ) add_definitions( -D_USE_MATH_DEFINES ) endif( WIN32 ) #################################################### # build unit tests INCLUDE (CTest) ENABLE_TESTING() option( BUILD_MARBLE_TESTS "Build unit tests" ON ) add_feature_info("Unit tests" BUILD_MARBLE_TESTS "Build unit tests. Toggle with BUILD_MARBLE_TESTS=YES/NO. 'make test' will run all.") if( BUILD_MARBLE_TESTS ) # SET (TEST_DATA_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/tests/test_data") #where unit test binaries should be installed to and run from # SET (MARBLE_TEST_DIR ${CMAKE_CURRENT_BINARY_DIR}/tests) endif( BUILD_MARBLE_TESTS ) #################################################### set (PEDANTIC FALSE CACHE BOOL "Determines if we should compile with -Wall -Werror.") set (WITH_DESIGNER_PLUGIN TRUE CACHE BOOL "Build plugins for Qt Designer") add_feature_info("Qt Designer plugins" WITH_DESIGNER_PLUGIN "Marble widget support in Qt Designer. Toggle with WITH_DESIGNER_PLUGIN=YES/NO") set(EXEC_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX} CACHE PATH "Base directory for executables and libraries" FORCE) if (NOT QT_PLUGINS_DIR) set(QT_PLUGINS_DIR ${CMAKE_INSTALL_LIBDIR}/plugins) endif() #################################################### # Detect default for the user configurable MARBLE_DATA_PATH option if(WIN32) set(data_dir data) set(plugin_dir plugins) elseif(APPLE) # needed for finding bundle path in e.g. katlasdir.h FIND_LIBRARY(APP_SERVICES_LIBRARY ApplicationServices ) MARK_AS_ADVANCED (APP_SERVICES_LIBRARY) SET(MAC_EXTRA_LIBS ${APP_SERVICES_LIBRARY}) # for Mac OS X, everything is put inside an application bundle SET (CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/) # path for library references SET (CMAKE_INSTALL_NAME_DIR @executable_path/lib) # install the Info.plist file install(FILES src/mac/Info.plist DESTINATION ${CMAKE_INSTALL_PREFIX}/Marble.app/Contents) #SET (lib_dir ${CMAKE_INSTALL_PREFIX}/Marble.app/Contents/MacOS/lib) SET (data_dir ${CMAKE_INSTALL_PREFIX}/Marble.app/Contents/MacOS/resources/data) SET (plugin_dir ${CMAKE_INSTALL_PREFIX}/Marble.app/Contents/MacOS/resources/plugins) elseif(CMAKE_SYSTEM_NAME STREQUAL Android) set(plugin_dir "${CMAKE_INSTALL_PREFIX}/assets/plugins") set(data_dir "${CMAKE_INSTALL_PREFIX}/assets/data") else() # Linux / bsd etc... set(data_dir ${CMAKE_INSTALL_FULL_DATAROOTDIR}/marble/data) set(plugin_dir ${CMAKE_INSTALL_FULL_LIBDIR}/marble/plugins) endif() if(NOT ICON_INSTALL_DIR) set(ICON_INSTALL_DIR ${CMAKE_INSTALL_FULL_DATAROOTDIR}/icons) endif(NOT ICON_INSTALL_DIR) if(NOT APPS_INSTALL_DIR) set(APPS_INSTALL_DIR ${CMAKE_INSTALL_FULL_DATAROOTDIR}/applications) endif(NOT APPS_INSTALL_DIR) if(NOT INCLUDE_INSTALL_DIR) set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include") endif() if (NOT MARBLE_DATA_PATH) set (MARBLE_DATA_PATH ${data_dir}) endif (NOT MARBLE_DATA_PATH) if (NOT MARBLE_PLUGIN_PATH) set (MARBLE_PLUGIN_PATH ${plugin_dir}) endif (NOT MARBLE_PLUGIN_PATH) if (NOT MARBLE_DATA_INSTALL_PATH) set (MARBLE_DATA_INSTALL_PATH ${MARBLE_DATA_PATH}) endif (NOT MARBLE_DATA_INSTALL_PATH) if (NOT MARBLE_PLUGIN_INSTALL_PATH) set (MARBLE_PLUGIN_INSTALL_PATH ${MARBLE_PLUGIN_PATH}) endif (NOT MARBLE_PLUGIN_INSTALL_PATH) #MESSAGE( STATUS, "MARBLE_PLUGIN_INSTALL_PATH: ${MARBLE_PLUGIN_INSTALL_PATH}" ) if(WIN32) set (STATIC_BUILD FALSE CACHE BOOL "Link to static Qt libs (win32 only)?") endif(WIN32) add_definitions( -DQT_USE_QSTRINGBUILDER -DQT_NO_SIGNALS_SLOTS_KEYWORDS -DQT_NO_URL_CAST_FROM_STRING -DQT_NO_CAST_TO_ASCII -DQT_NO_CAST_FROM_BYTEARRAY -DQT_STRICT_ITERATORS + -DQT_DISABLE_DEPRECATED_BEFORE=0x050700 ) #################################################### # Options for static build if(STATIC_BUILD) add_definitions(-DSTATIC_BUILD=1) endif(STATIC_BUILD) ############################################################# if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER MATCHES "icc") # Its good programming practice to build with no warnings... add_definitions( -Wall -Wextra -Wundef -Wnon-virtual-dtor -Woverloaded-virtual -Wno-long-long -Wchar-subscripts -Wcast-align -Wpointer-arith -Wformat-security ) # In pedantic mode, treat warnings as errors if (PEDANTIC) add_definitions( -Werror ) endif (PEDANTIC) endif (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_C_COMPILER MATCHES "icc") ############################################################# # Add a compiler def so that we can conditionally compile # code in debug mode only (e.g. extra console messages) IF (CMAKE_BUILD_TYPE MATCHES Debug) IF(NOT MINGW) ADD_DEFINITIONS(-DDEBUG) ELSE(NOT MINGW) REMOVE_DEFINITIONS( -DQT_NO_DEBUG ) ENDIF(NOT MINGW) ENDIF (CMAKE_BUILD_TYPE MATCHES Debug) #################################################### # on Win32 set the debug postfix if(WIN32) # distinguish between debug and release plugin SET(CMAKE_DEBUG_POSTFIX "d") endif(WIN32) #################################################### #################################################### # Add global ECM & KF5 settings set(REQUIRED_ECM_VERSION 1.7.0) set(KDE_INSTALL_DIRS_NO_DEPRECATED TRUE) set(KDE_SKIP_UNINSTALL_TARGET ON CACHE BOOL "KDE uninstall target must be disabled") set(REQUIRED_KF5_VERSION 5.7.0) #################################################### # Add the include directories include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/marble ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/marble/projections ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/marble/geodata ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/marble/geodata/data ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/marble/geodata/graphicsitem ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/marble/geodata/handlers/dgml ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/marble/geodata/parser ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/marble/geodata/writer ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/marble/geodata/scene ${CMAKE_CURRENT_SOURCE_DIR}/src/lib/marble/graphicsview ${CMAKE_CURRENT_BINARY_DIR}/src ${CMAKE_CURRENT_BINARY_DIR}/src/lib/marble ) #################################################### # Descend into subdirectories add_subdirectory(doc) add_subdirectory(src) add_subdirectory(data) include(DistTarget) add_subdirectory(tests) option(BUILD_MARBLE_TOOLS "Build various tools related to Marble" OFF) add_feature_info("Marble tools" BUILD_MARBLE_TOOLS "Build various Marble tools for e.g. file format conversion. Toggle with BUILD_MARBLE_TOOLS=YES/NO.") if(BUILD_MARBLE_TOOLS) add_subdirectory(tools) endif() option(BUILD_MARBLE_EXAMPLES "Build C++ examples showing how to use the Marble library" OFF) add_feature_info("Marble library C++ examples" BUILD_MARBLE_EXAMPLES "Build C++ examples showing how to use the Marble library. Toggle with BUILD_MARBLE_EXAMPLES=YES/NO.") if(BUILD_MARBLE_EXAMPLES) add_subdirectory(examples/cpp) endif() #################################################### # Install extra files install(FILES LICENSE.txt DESTINATION ${MARBLE_DATA_INSTALL_PATH}) ############################################################ # Uninstall stuff CONFIGURE_FILE( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/templates/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) ADD_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") include(MarbleCPackOptions) marble_feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES) diff --git a/src/lib/marble/AbstractDataPluginModel.cpp b/src/lib/marble/AbstractDataPluginModel.cpp index 2522f160a..bc9ce5b73 100644 --- a/src/lib/marble/AbstractDataPluginModel.cpp +++ b/src/lib/marble/AbstractDataPluginModel.cpp @@ -1,673 +1,673 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2009 Bastian Holst // // Self #include "AbstractDataPluginModel.h" // Qt #include #include #include #include #include #include #include #include // Marble #include "MarbleDebug.h" #include "AbstractDataPluginItem.h" #include "CacheStoragePolicy.h" #include "GeoDataCoordinates.h" #include "GeoDataLatLonAltBox.h" #include "HttpDownloadManager.h" #include "MarbleModel.h" #include "MarbleDirs.h" #include "ViewportParams.h" #include namespace Marble { const QString descriptionPrefix( "description_" ); // Time between two tried description file downloads (we decided not to download anything) in ms const int timeBetweenTriedDownloads = 500; // Time between two real description file downloads in ms const int timeBetweenDownloads = 1500; // The factor describing how much the box has to be changed to download a new description file. // A higher factor means more downloads. const qreal boxComparisonFactor = 16.0; // Separator to separate the id of the item from the file type const QChar fileIdSeparator = QLatin1Char('_'); class FavoritesModel; class AbstractDataPluginModelPrivate { public: AbstractDataPluginModelPrivate( const QString& name, const MarbleModel *marbleModel, AbstractDataPluginModel * parent ); ~AbstractDataPluginModelPrivate(); QString generateFilename( const QString& id, const QString& type ) const; QString generateFilepath( const QString& id, const QString& type ) const; void updateFavoriteItems(); AbstractDataPluginModel *m_parent; const QString m_name; const MarbleModel *const m_marbleModel; GeoDataLatLonAltBox m_lastBox; GeoDataLatLonAltBox m_downloadedBox; qint32 m_lastNumber; qint32 m_downloadedNumber; QString m_currentPlanetId; QList m_itemSet; QHash m_downloadingItems; QList m_displayedItems; QTimer m_downloadTimer; quint32 m_descriptionFileNumber; QHash m_itemSettings; QStringList m_favoriteItems; bool m_favoriteItemsOnly; CacheStoragePolicy m_storagePolicy; HttpDownloadManager m_downloadManager; FavoritesModel* m_favoritesModel; QMetaObject m_metaObject; bool m_hasMetaObject; bool m_needsSorting; }; class FavoritesModel : public QAbstractListModel { public: AbstractDataPluginModelPrivate* d; explicit FavoritesModel( AbstractDataPluginModelPrivate* d, QObject* parent = 0 ); int rowCount ( const QModelIndex & parent = QModelIndex() ) const override; QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const override; void reset(); QHash roleNames() const override; private: QHash m_roleNames; }; AbstractDataPluginModelPrivate::AbstractDataPluginModelPrivate( const QString& name, const MarbleModel *marbleModel, AbstractDataPluginModel * parent ) : m_parent( parent ), m_name( name ), m_marbleModel( marbleModel ), m_lastBox(), m_downloadedBox(), m_lastNumber( 0 ), m_downloadedNumber( 0 ), m_currentPlanetId( marbleModel->planetId() ), m_downloadTimer( m_parent ), m_descriptionFileNumber( 0 ), m_itemSettings(), m_favoriteItemsOnly( false ), m_storagePolicy(MarbleDirs::localPath() + QLatin1String("/cache/") + m_name + QLatin1Char('/')), m_downloadManager( &m_storagePolicy ), m_favoritesModel( 0 ), m_hasMetaObject( false ), m_needsSorting( false ) { } AbstractDataPluginModelPrivate::~AbstractDataPluginModelPrivate() { QList::iterator lIt = m_itemSet.begin(); QList::iterator const lItEnd = m_itemSet.end(); for (; lIt != lItEnd; ++lIt ) { (*lIt)->deleteLater(); } QHash::iterator hIt = m_downloadingItems.begin(); QHash::iterator const hItEnd = m_downloadingItems.end(); for (; hIt != hItEnd; ++hIt ) { (*hIt)->deleteLater(); } m_storagePolicy.clearCache(); } void AbstractDataPluginModelPrivate::updateFavoriteItems() { if ( m_favoriteItemsOnly ) { foreach( const QString &id, m_favoriteItems ) { if ( !m_parent->findItem( id ) ) { m_parent->getItem( id ); } } } } void AbstractDataPluginModel::themeChanged() { if ( d->m_currentPlanetId != d->m_marbleModel->planetId() ) { clear(); d->m_currentPlanetId = d->m_marbleModel->planetId(); } } static bool lessThanByPointer( const AbstractDataPluginItem *item1, const AbstractDataPluginItem *item2 ) { if( item1 && item2 ) { // Compare by sticky and favorite status (sticky first, then favorites), last by operator< bool const sticky1 = item1->isSticky(); bool const favorite1 = item1->isFavorite(); if ( sticky1 != item2->isSticky() ) { return sticky1; } else if ( favorite1 != item2->isFavorite() ) { return favorite1; } else { return item1->operator<( item2 ); } } else { return false; } } FavoritesModel::FavoritesModel( AbstractDataPluginModelPrivate *_d, QObject* parent ) : QAbstractListModel( parent ), d(_d) { QHash roles; int const size = d->m_hasMetaObject ? d->m_metaObject.propertyCount() : 0; for ( int i=0; im_metaObject.property( i ); roles[Qt::UserRole+i] = property.name(); } roles[Qt::DisplayRole] = "display"; roles[Qt::DecorationRole] = "decoration"; m_roleNames = roles; } int FavoritesModel::rowCount ( const QModelIndex &parent ) const { if ( parent.isValid() ) { return 0; } int count = 0; foreach( AbstractDataPluginItem* item, d->m_itemSet ) { if ( item->initialized() && item->isFavorite() ) { ++count; } } return count; } QVariant FavoritesModel::data( const QModelIndex &index, int role ) const { int const row = index.row(); if ( row >= 0 && row < rowCount() ) { int count = 0; foreach( AbstractDataPluginItem* item, d->m_itemSet ) { if ( item->initialized() && item->isFavorite() ) { if ( count == row ) { QString const roleName = roleNames().value( role ); return item->property(roleName.toLatin1().constData()); } ++count; } } } return QVariant(); } void FavoritesModel::reset() { beginResetModel(); endResetModel(); } QHash FavoritesModel::roleNames() const { return m_roleNames; } AbstractDataPluginModel::AbstractDataPluginModel( const QString &name, const MarbleModel *marbleModel, QObject *parent ) : QObject( parent ), d( new AbstractDataPluginModelPrivate( name, marbleModel, this ) ) { Q_ASSERT( marbleModel != 0 ); // Initializing file and download System connect( &d->m_downloadManager, SIGNAL(downloadComplete(QString,QString)), this , SLOT(processFinishedJob(QString,QString)) ); connect( marbleModel, SIGNAL(themeChanged(QString)), this, SLOT(themeChanged()) ); // We want to download a new description file every timeBetweenDownloads ms connect( &d->m_downloadTimer, SIGNAL(timeout()), this, SLOT(handleChangedViewport()), Qt::QueuedConnection ); d->m_downloadTimer.start( timeBetweenDownloads ); } AbstractDataPluginModel::~AbstractDataPluginModel() { delete d; } const MarbleModel *AbstractDataPluginModel::marbleModel() const { return d->m_marbleModel; } QList AbstractDataPluginModel::items( const ViewportParams *viewport, qint32 number ) { GeoDataLatLonAltBox currentBox = viewport->viewLatLonAltBox(); QList list; Q_ASSERT( !d->m_displayedItems.contains( 0 ) && "Null item in m_displayedItems. Please report a bug to marble-devel@kde.org" ); Q_ASSERT( !d->m_itemSet.contains( 0 ) && "Null item in m_itemSet. Please report a bug to marble-devel@kde.org" ); QList candidates = d->m_displayedItems + d->m_itemSet; if ( d->m_needsSorting ) { // Both the candidates list and the list of all items need to be sorted std::sort( candidates.begin(), candidates.end(), lessThanByPointer ); std::sort( d->m_itemSet.begin(), d->m_itemSet.end(), lessThanByPointer ); d->m_needsSorting = false; } QList::const_iterator i = candidates.constBegin(); QList::const_iterator end = candidates.constEnd(); // Items that are already shown have the highest priority for (; i != end && list.size() < number; ++i ) { // Only show items that are initialized if( !(*i)->initialized() ) { continue; } // Hide non-favorite items if necessary if( d->m_favoriteItemsOnly && !(*i)->isFavorite() ) { continue; } (*i)->setProjection( viewport ); if( (*i)->positions().isEmpty() ) { continue; } if ( list.contains( *i ) ) { continue; } // If the item was added initially at a nearer position, they don't have priority, // because we zoomed out since then. bool const alreadyDisplayed = d->m_displayedItems.contains( *i ); if ( !alreadyDisplayed || (*i)->addedAngularResolution() >= viewport->angularResolution() || (*i)->isSticky() ) { bool collides = false; int const length = list.length(); for ( int j=0; !collides && jboundingRects() ) { foreach( const QRectF &itemRect, (*i)->boundingRects() ) { if ( rect.intersects( itemRect ) ) collides = true; } } } if ( !collides ) { list.append( *i ); (*i)->setSettings( d->m_itemSettings ); // We want to save the angular resolution of the first time the item got added. if( !alreadyDisplayed ) { (*i)->setAddedAngularResolution( viewport->angularResolution() ); } } } // TODO: Do we have to cleanup at some point? The list of all items keeps growing } d->m_lastBox = currentBox; d->m_lastNumber = number; d->m_displayedItems = list; return list; } QList AbstractDataPluginModel::whichItemAt( const QPoint& curpos ) { QList itemsAt; const QPointF curposF(curpos); foreach( AbstractDataPluginItem* item, d->m_displayedItems ) { if (item && item->contains(curposF)) { itemsAt.append( item ); } } return itemsAt; } void AbstractDataPluginModel::parseFile( const QByteArray& file ) { Q_UNUSED( file ); } void AbstractDataPluginModel::downloadItem( const QUrl& url, const QString& type, AbstractDataPluginItem *item ) { if( !item ) { return; } QString id = d->generateFilename( item->id(), type ); d->m_downloadManager.addJob( url, id, id, DownloadBrowse ); d->m_downloadingItems.insert( id, item ); } void AbstractDataPluginModel::downloadDescriptionFile( const QUrl& url ) { if( !url.isEmpty() ) { QString name( descriptionPrefix ); name += QString::number( d->m_descriptionFileNumber ); d->m_downloadManager.addJob( url, name, name, DownloadBrowse ); d->m_descriptionFileNumber++; } } void AbstractDataPluginModel::addItemToList( AbstractDataPluginItem *item ) { addItemsToList( QList() << item ); } void AbstractDataPluginModel::addItemsToList( const QList &items ) { bool needsUpdate = false; bool favoriteChanged = false; foreach( AbstractDataPluginItem *item, items ) { if( !item ) { continue; } // If the item is already in our list, don't add it. if ( d->m_itemSet.contains( item ) ) { continue; } if( itemExists( item->id() ) ) { item->deleteLater(); continue; } mDebug() << "New item " << item->id(); // This find the right position in the sorted to insert the new item - QList::iterator i = qLowerBound( d->m_itemSet.begin(), + QList::iterator i = std::lower_bound( d->m_itemSet.begin(), d->m_itemSet.end(), item, lessThanByPointer ); // Insert the item on the right position in the list d->m_itemSet.insert( i, item ); connect( item, SIGNAL(stickyChanged()), this, SLOT(scheduleItemSort()) ); connect( item, SIGNAL(destroyed(QObject*)), this, SLOT(removeItem(QObject*)) ); connect( item, SIGNAL(updated()), this, SIGNAL(itemsUpdated()) ); connect( item, SIGNAL(favoriteChanged(QString,bool)), this, SLOT(favoriteItemChanged(QString,bool)) ); if ( !needsUpdate && item->initialized() ) { needsUpdate = true; } if ( !favoriteChanged && item->initialized() && item->isFavorite() ) { favoriteChanged = true; } } if ( favoriteChanged && d->m_favoritesModel ) { d->m_favoritesModel->reset(); } if ( needsUpdate ) { emit itemsUpdated(); } } void AbstractDataPluginModel::getItem( const QString & ) { qWarning() << "Retrieving items by identifier is not implemented by this plugin"; } void AbstractDataPluginModel::setFavoriteItems( const QStringList& list ) { if ( d->m_favoriteItems != list) { d->m_favoriteItems = list; d->updateFavoriteItems(); if ( d->m_favoritesModel ) { d->m_favoritesModel->reset(); } emit favoriteItemsChanged( d->m_favoriteItems ); } } QStringList AbstractDataPluginModel::favoriteItems() const { return d->m_favoriteItems; } void AbstractDataPluginModel::setFavoriteItemsOnly( bool favoriteOnly ) { if ( isFavoriteItemsOnly() != favoriteOnly ) { d->m_favoriteItemsOnly = favoriteOnly; d->updateFavoriteItems(); emit favoriteItemsOnlyChanged(); } } bool AbstractDataPluginModel::isFavoriteItemsOnly() const { return d->m_favoriteItemsOnly; } QObject *AbstractDataPluginModel::favoritesModel() { if ( !d->m_favoritesModel ) { d->m_favoritesModel = new FavoritesModel( d, this ); d->updateFavoriteItems(); } return d->m_favoritesModel; } void AbstractDataPluginModel::favoriteItemChanged( const QString& id, bool isFavorite ) { QStringList favorites = d->m_favoriteItems; if ( isFavorite ) { if ( !favorites.contains(id) ) favorites.append( id ); } else { favorites.removeOne( id ); } setFavoriteItems( favorites ); scheduleItemSort(); } void AbstractDataPluginModel::scheduleItemSort() { d->m_needsSorting = true; } QString AbstractDataPluginModelPrivate::generateFilename( const QString& id, const QString& type ) const { QString name; name += id; name += fileIdSeparator; name += type; return name; } QString AbstractDataPluginModelPrivate::generateFilepath( const QString& id, const QString& type ) const { return MarbleDirs::localPath() + QLatin1String("/cache/") + m_name + QLatin1Char('/') + generateFilename(id, type); } AbstractDataPluginItem *AbstractDataPluginModel::findItem( const QString& id ) const { foreach ( AbstractDataPluginItem *item, d->m_itemSet ) { if( item->id() == id ) { return item; } } return 0; } bool AbstractDataPluginModel::itemExists( const QString& id ) const { return findItem( id ); } void AbstractDataPluginModel::setItemSettings(const QHash &itemSettings) { d->m_itemSettings = itemSettings; } void AbstractDataPluginModel::handleChangedViewport() { if( d->m_favoriteItemsOnly ) { return; } // All this is to prevent to often downloads if( d->m_lastNumber != 0 // We don't need to download if nothing changed && ( !( d->m_downloadedBox == d->m_lastBox ) || d->m_downloadedNumber != d->m_lastNumber ) // We try to filter little changes of the bounding box && ( fabs( d->m_downloadedBox.east() - d->m_lastBox.east() ) * boxComparisonFactor > d->m_lastBox.width() || fabs( d->m_downloadedBox.south() - d->m_lastBox.south() ) * boxComparisonFactor > d->m_lastBox.height() || fabs( d->m_downloadedBox.north() - d->m_lastBox.north() ) * boxComparisonFactor > d->m_lastBox.height() || fabs( d->m_downloadedBox.west() - d->m_lastBox.west() ) * boxComparisonFactor > d->m_lastBox.width() ) ) { // We will wait a little bit longer to start the // next download as we will really download something now. d->m_downloadTimer.setInterval( timeBetweenDownloads ); // Save the download parameter d->m_downloadedBox = d->m_lastBox; d->m_downloadedNumber = d->m_lastNumber; // Get items getAdditionalItems( d->m_lastBox, d->m_lastNumber ); } else { // Don't wait to long to start the next download as we decided not to download anything. // This will enhance response. d->m_downloadTimer.setInterval( timeBetweenTriedDownloads ); } } void AbstractDataPluginModel::processFinishedJob( const QString& relativeUrlString, const QString& id ) { Q_UNUSED( relativeUrlString ); if( id.startsWith( descriptionPrefix ) ) { parseFile( d->m_storagePolicy.data( id ) ); } else { // The downloaded file contains item data. // Splitting the id in itemId and fileType QStringList fileInformation = id.split( fileIdSeparator ); if( fileInformation.size() < 2) { mDebug() << "Strange file information " << id; return; } QString itemId = fileInformation.at( 0 ); fileInformation.removeAt( 0 ); QString fileType = fileInformation.join( QString( fileIdSeparator ) ); // Searching for the right item in m_downloadingItems QHash::iterator i = d->m_downloadingItems.find( id ); if( i != d->m_downloadingItems.end() ) { if( itemId != (*i)->id() ) { return; } (*i)->addDownloadedFile( d->generateFilepath( itemId, fileType ), fileType ); d->m_downloadingItems.erase( i ); } } } void AbstractDataPluginModel::removeItem( QObject *item ) { AbstractDataPluginItem * pluginItem = qobject_cast( item ); d->m_itemSet.removeAll( pluginItem ); QHash::iterator i; for( i = d->m_downloadingItems.begin(); i != d->m_downloadingItems.end(); ++i ) { if( *i == pluginItem ) { i = d->m_downloadingItems.erase( i ); } } } void AbstractDataPluginModel::clear() { d->m_displayedItems.clear(); QList::iterator iter = d->m_itemSet.begin(); QList::iterator const end = d->m_itemSet.end(); for (; iter != end; ++iter ) { (*iter)->deleteLater(); } d->m_itemSet.clear(); d->m_lastBox = GeoDataLatLonAltBox(); d->m_downloadedBox = GeoDataLatLonAltBox(); d->m_downloadedNumber = 0; emit itemsUpdated(); } void AbstractDataPluginModel::registerItemProperties( const QMetaObject &item ) { d->m_metaObject = item; d->m_hasMetaObject = true; } } // namespace Marble #include "moc_AbstractDataPluginModel.cpp" diff --git a/src/lib/marble/TourWidget.cpp b/src/lib/marble/TourWidget.cpp index 20cba88bc..6e82b4ff2 100644 --- a/src/lib/marble/TourWidget.cpp +++ b/src/lib/marble/TourWidget.cpp @@ -1,982 +1,982 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2013 Mihail Ivchenko // Copyright 2014 Sanjiban Bairagya // #include "TourWidget.h" #include "FlyToEditWidget.h" #include "TourControlEditWidget.h" #include "WaitEditWidget.h" #include "SoundCueEditWidget.h" #include "TourItemDelegate.h" #include "ui_TourWidget.h" #include "GeoDataPlacemark.h" #include "GeoDataDocument.h" #include "GeoDataLookAt.h" #include "GeoDataPlaylist.h" #include "GeoDataTour.h" #include "GeoDataTreeModel.h" #include "GeoDataTypes.h" #include "GeoDataFlyTo.h" #include "GeoDataWait.h" #include "GeoDataCamera.h" #include "GeoDataTourControl.h" #include "GeoDataSoundCue.h" #include "GeoDataCreate.h" #include "GeoDataUpdate.h" #include "GeoDataDelete.h" #include "GeoDataChange.h" #include "GeoDataAnimatedUpdate.h" #include "GeoDataDocumentWriter.h" #include "KmlElementDictionary.h" #include "MarbleModel.h" #include "MarblePlacemarkModel.h" #include "MarbleWidget.h" #include "ParsingRunnerManager.h" #include "TourPlayback.h" #include "MovieCapture.h" #include "TourCaptureDialog.h" #include "MarbleDebug.h" #include "PlaybackFlyToItem.h" #include "EditPlacemarkDialog.h" #include "MarbleDirs.h" #include "GeoDataStyle.h" #include "GeoDataIconStyle.h" #include #include #include #include #include #include #include #include #include #include #include namespace Marble { class TourWidgetPrivate { public: explicit TourWidgetPrivate( TourWidget *parent ); ~TourWidgetPrivate(); GeoDataFeature *getPlaylistFeature() const; void updateRootIndex(); public: void openFile(); bool openFile( const QString &filename ); void createTour(); void saveTour(); void saveTourAs(); void mapCenterOn(const QModelIndex &index ); void addFlyTo(); void addWait(); void addSoundCue(); void addPlacemark(); void addRemovePlacemark(); void addChangePlacemark(); void addTourPrimitive(GeoDataTourPrimitive *primitive ); void deleteSelected(); void updateButtonsStates(); void moveUp(); void moveDown(); void captureTour(); void handlePlaybackProgress( const double position ); void handlePlaybackFinish(); GeoDataObject *rootIndexObject() const; private: GeoDataTour* findTour( GeoDataFeature* feature ) const; bool openDocument( GeoDataDocument *document ); bool saveTourAs( const QString &filename ); bool overrideModifications(); public: TourWidget *q; MarbleWidget *m_widget; Ui::TourWidget m_tourUi; TourCaptureDialog *m_tourCaptureDialog; TourPlayback m_playback; TourItemDelegate *m_delegate; bool m_isChanged; bool m_playState; bool m_isLoopingStopped; GeoDataDocument* m_document; QAction *m_actionToggleLoopPlay; QToolButton *m_addPrimitiveButton; QAction *m_actionAddFlyTo; QAction *m_actionAddWait; QAction *m_actionAddSoundCue; QAction *m_actionAddPlacemark; QAction *m_actionAddRemovePlacemark; QAction *m_actionAddChangePlacemark; }; TourWidgetPrivate::TourWidgetPrivate( TourWidget *parent ) : q( parent ), m_widget( 0 ), m_playback( 0 ), m_delegate( 0 ), m_isChanged( false ), m_playState( false ), m_document( 0 ), m_addPrimitiveButton( new QToolButton ) { m_tourUi.setupUi( parent ); m_tourUi.m_actionRecord->setEnabled( false ); QAction *separator = m_tourUi.m_toolBarControl->insertSeparator( m_tourUi.m_actionMoveUp ); m_addPrimitiveButton->setIcon(QIcon(QStringLiteral(":/marble/flag.png"))); m_addPrimitiveButton->setToolTip( QObject::tr( "Add FlyTo" ) ); m_addPrimitiveButton->setPopupMode( QToolButton::MenuButtonPopup ); QMenu *addPrimitiveMenu = new QMenu(q); m_actionAddFlyTo = new QAction(QIcon(QStringLiteral(":/marble/flag.png")), QObject::tr("Add FlyTo"), addPrimitiveMenu); addPrimitiveMenu->addAction( m_actionAddFlyTo ); m_actionAddWait = new QAction(QIcon(QStringLiteral(":/marble/player-time.png")), QObject::tr("Add Wait"), addPrimitiveMenu); addPrimitiveMenu->addAction( m_actionAddWait ); m_actionAddSoundCue = new QAction(QIcon(QStringLiteral(":/marble/audio-x-generic.png")), QObject::tr("Add SoundCue"), addPrimitiveMenu); addPrimitiveMenu->addAction( m_actionAddSoundCue ); addPrimitiveMenu->addSeparator(); m_actionAddPlacemark = new QAction(QIcon(QStringLiteral(":/icons/add-placemark.png")), QObject::tr("Add Placemark"), addPrimitiveMenu); addPrimitiveMenu->addAction( m_actionAddPlacemark ); m_actionAddRemovePlacemark = new QAction(QIcon(QStringLiteral(":/icons/remove.png")), QObject::tr("Remove placemark"), addPrimitiveMenu); addPrimitiveMenu->addAction( m_actionAddRemovePlacemark ); m_actionAddChangePlacemark = new QAction(QIcon(QStringLiteral(":/marble/document-edit.png")), QObject::tr("Change placemark"), addPrimitiveMenu); addPrimitiveMenu->addAction( m_actionAddChangePlacemark ); m_actionToggleLoopPlay = new QAction( QObject::tr( "Loop" ), m_tourUi.m_slider ); m_actionToggleLoopPlay->setCheckable( true ); m_actionToggleLoopPlay->setChecked( false ); m_tourUi.m_slider->setContextMenuPolicy( Qt::ActionsContextMenu ); m_tourUi.m_slider->addAction( m_actionToggleLoopPlay ); m_addPrimitiveButton->setMenu( addPrimitiveMenu ); m_addPrimitiveButton->setEnabled( false ); m_tourUi.m_toolBarControl->insertWidget( separator, m_addPrimitiveButton ); QObject::connect( m_tourUi.m_listView, SIGNAL(activated(QModelIndex)), q, SLOT(mapCenterOn(QModelIndex)) ); QObject::connect( m_addPrimitiveButton, SIGNAL(clicked()), q, SLOT(addFlyTo()) ); QObject::connect( m_actionAddFlyTo, SIGNAL(triggered()), q, SLOT(addFlyTo()) ); QObject::connect( m_actionAddWait, SIGNAL(triggered()), q, SLOT(addWait()) ); QObject::connect( m_actionAddSoundCue, SIGNAL(triggered()), q, SLOT(addSoundCue()) ); QObject::connect( m_actionAddPlacemark, SIGNAL(triggered()), q, SLOT(addPlacemark()) ); QObject::connect( m_actionAddRemovePlacemark, SIGNAL(triggered()), q, SLOT(addRemovePlacemark()) ); QObject::connect( m_actionAddChangePlacemark, SIGNAL(triggered()), q, SLOT(addChangePlacemark()) ); QObject::connect( m_tourUi.m_actionDelete, SIGNAL(triggered()), q, SLOT(deleteSelected()) ); QObject::connect( m_tourUi.m_actionMoveUp, SIGNAL(triggered()), q, SLOT(moveUp()) ); QObject::connect( m_tourUi.m_actionMoveDown, SIGNAL(triggered()), q, SLOT(moveDown()) ); QObject::connect( m_tourUi.m_actionNewTour, SIGNAL(triggered()), q, SLOT(createTour()) ); QObject::connect( m_tourUi.m_actionOpenTour, SIGNAL(triggered()), q, SLOT(openFile()) ); QObject::connect( m_tourUi.m_actionSaveTour, SIGNAL(triggered()), q, SLOT(saveTour()) ); QObject::connect( m_tourUi.m_actionSaveTourAs, SIGNAL(triggered()), q, SLOT(saveTourAs()) ); QObject::connect( m_tourUi.m_actionRecord, SIGNAL(triggered()), q, SLOT(captureTour()) ); QObject::connect( &m_playback, SIGNAL(finished()), q, SLOT(stopPlaying()) ); QObject::connect( &m_playback, SIGNAL(itemFinished(int)), q, SLOT(setHighlightedItemIndex(int)) ); } TourWidgetPrivate::~TourWidgetPrivate() { delete m_delegate; } TourWidget::TourWidget( QWidget *parent, Qt::WindowFlags flags ) : QWidget( parent, flags ), d( new TourWidgetPrivate( this ) ) { layout()->setMargin( 0 ); connect( d->m_tourUi.actionPlay, SIGNAL(triggered()), this, SLOT(togglePlaying()) ); connect( d->m_tourUi.actionStop, SIGNAL(triggered()), this, SLOT(stopLooping()) ); connect( d->m_tourUi.actionStop, SIGNAL(triggered()), this, SLOT(stopPlaying()) ); connect( d->m_tourUi.m_slider, SIGNAL(sliderMoved(int)), this, SLOT(handleSliderMove(int)) ); d->m_tourUi.m_toolBarPlayback->setDisabled( true ); d->m_tourUi.m_slider->setDisabled( true ); d->m_tourUi.m_listView->installEventFilter( this ); } TourWidget::~TourWidget() { delete d; } bool TourWidget::eventFilter( QObject *watched, QEvent *event ) { Q_UNUSED(watched); Q_ASSERT( watched == d->m_tourUi.m_listView ); GeoDataObject *rootObject = d->rootIndexObject(); if ( !rootObject ) { return false; } if ( event->type() == QEvent::KeyPress ) { QKeyEvent *key = static_cast( event ); QModelIndexList selectedIndexes = d->m_tourUi.m_listView->selectionModel()->selectedIndexes(); if ( key->key() == Qt::Key_Delete ) { if ( !selectedIndexes.isEmpty() ) { deleteSelected(); } return true; } if ( key->key() == Qt::Key_PageDown && key->modifiers().testFlag( Qt::ControlModifier ) && !selectedIndexes.isEmpty() ) { QModelIndexList::iterator end = selectedIndexes.end() - 1; if ( rootObject && rootObject->nodeType() == GeoDataTypes::GeoDataPlaylistType ) { GeoDataPlaylist *playlist = static_cast( rootObject ); if ( end->row() != playlist->size() - 1 ) { moveDown(); } } return true; } if ( key->key() == Qt::Key_PageUp && key->modifiers().testFlag( Qt::ControlModifier ) && !selectedIndexes.isEmpty() ) { QModelIndexList::iterator start = selectedIndexes.begin(); if ( start->row() != 0 ) { moveUp(); } return true; } } return false; } void TourWidget::setMarbleWidget( MarbleWidget *widget ) { d->m_widget = widget; delete d->m_delegate; d->m_delegate = new TourItemDelegate( d->m_tourUi.m_listView, d->m_widget, this ); connect( d->m_delegate, SIGNAL(edited(QModelIndex)), this, SLOT(updateDuration()) ); connect( d->m_delegate, SIGNAL(edited(QModelIndex)), &d->m_playback, SLOT(updateTracks()) ); d->m_tourUi.m_listView->setItemDelegate( d->m_delegate ); } void TourWidget::togglePlaying() { if( !d->m_playState ){ d->m_playState = true; startPlaying(); } else { d->m_playState = false; pausePlaying(); } } void TourWidget::startPlaying() { setHighlightedItemIndex( 0 ); d->m_isLoopingStopped = false; d->m_playback.play(); d->m_tourUi.actionPlay->setIcon(QIcon(QStringLiteral(":/marble/playback-pause.png"))); d->m_tourUi.actionPlay->setEnabled( true ); d->m_tourUi.actionStop->setEnabled( true ); d->m_tourUi.m_actionRecord->setEnabled( false ); d->m_delegate->setEditable( false ); d->m_addPrimitiveButton->setEnabled( false ); d->m_playState = true; } void TourWidget::pausePlaying() { d->m_playback.pause(); d->m_tourUi.actionPlay->setIcon(QIcon(QStringLiteral(":/marble/playback-play.png"))); d->m_tourUi.actionPlay->setEnabled( true ); d->m_tourUi.actionStop->setEnabled( true ); } void TourWidget::stopPlaying() { removeHighlight(); d->m_playback.stop(); d->m_tourUi.actionPlay->setIcon(QIcon(QStringLiteral(":/marble/playback-play.png"))); d->m_tourUi.actionPlay->setEnabled( true ); d->m_tourUi.m_actionRecord->setEnabled( true ); d->m_tourUi.actionStop->setEnabled( false ); d->m_playState = false; d->m_delegate->setEditable( true ); d->m_addPrimitiveButton->setEnabled( true ); // Loop if the option ( m_actionLoopPlay ) is checked if ( d->m_actionToggleLoopPlay->isChecked() && !d->m_isLoopingStopped ) { startPlaying(); } } void TourWidget::stopLooping() { d->m_isLoopingStopped = true; } void TourWidget::closeEvent( QCloseEvent *event ) { if ( !d->m_document || !d->m_isChanged ) { event->accept(); return; } const int result = QMessageBox::question( d->m_widget, QObject::tr( "Save tour" ), QObject::tr( "There are unsaved Tours. Do you want to save your changes?" ), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel ); switch ( result ) { case QMessageBox::Save: d->saveTour(); event->accept(); break; case QMessageBox::Discard: event->accept(); break; case QMessageBox::Cancel: event->ignore(); } } void TourWidget::handleSliderMove( int value ) { removeHighlight(); d->m_playback.seek( value / 100.0 ); QTime nullTime( 0, 0, 0 ); QTime time = nullTime.addSecs( value / 100.0 ); d->m_tourUi.m_elapsedTime->setText(QString("%L1:%L2").arg(time.minute(), 2, 10, QLatin1Char('0')).arg(time.second(), 2, 10, QLatin1Char('0'))); } void TourWidgetPrivate::openFile() { if ( overrideModifications() ) { QString const filename = QFileDialog::getOpenFileName( q, QObject::tr( "Open Tour" ), QDir::homePath(), QObject::tr( "KML Tours (*.kml)" ) ); if ( !filename.isEmpty() ) { ParsingRunnerManager manager( m_widget->model()->pluginManager() ); GeoDataDocument* document = manager.openFile( filename ); m_playback.setBaseUrl( QUrl::fromLocalFile( filename ) ); openDocument( document ); } } } bool TourWidgetPrivate::openFile( const QString &filename ) { if ( overrideModifications() ) { if ( !filename.isEmpty() ) { ParsingRunnerManager manager( m_widget->model()->pluginManager() ); GeoDataDocument* document = manager.openFile( filename ); m_playback.setBaseUrl( QUrl::fromLocalFile( filename ) ); return openDocument( document ); } } return false; } GeoDataTour *TourWidgetPrivate::findTour( GeoDataFeature *feature ) const { if ( feature && feature->nodeType() == GeoDataTypes::GeoDataTourType ) { return static_cast( feature ); } GeoDataContainer *container = dynamic_cast( feature ); if ( container ) { QVector::Iterator end = container->end(); QVector::Iterator iter = container->begin(); for( ; iter != end; ++iter ) { GeoDataTour *tour = findTour( *iter ); if ( tour ) { return tour; } } } return 0; } void TourWidgetPrivate::mapCenterOn( const QModelIndex &index ) { QVariant coordinatesVariant = m_widget->model()->treeModel()->data( index, MarblePlacemarkModel::CoordinateRole ); if ( !coordinatesVariant.isNull() ) { GeoDataCoordinates const coordinates = coordinatesVariant.value(); GeoDataLookAt lookat; lookat.setCoordinates( coordinates ); lookat.setRange( coordinates.altitude() ); m_widget->flyTo( lookat, Instant ); } } void TourWidgetPrivate::addFlyTo() { GeoDataFlyTo *flyTo = new GeoDataFlyTo(); GeoDataLookAt *lookat = new GeoDataLookAt( m_widget->lookAt() ); lookat->setAltitude( lookat->range() ); flyTo->setView( lookat ); bool isMainTrackEmpty = m_playback.mainTrackSize() == 0; flyTo->setDuration( isMainTrackEmpty ? 0.0 : 1.0 ); addTourPrimitive( flyTo ); } void TourWidgetPrivate::addWait() { GeoDataWait *wait = new GeoDataWait(); wait->setDuration( 1.0 ); addTourPrimitive( wait ); } void TourWidgetPrivate::addSoundCue() { GeoDataSoundCue *soundCue = new GeoDataSoundCue(); addTourPrimitive( soundCue ); } void TourWidgetPrivate::addPlacemark() { // Get the normalized coordinates of the focus point. There will be automatically added a new // placemark. qreal lat = m_widget->focusPoint().latitude(); qreal lon = m_widget->focusPoint().longitude(); GeoDataCoordinates::normalizeLonLat( lon, lat ); GeoDataDocument *document = new GeoDataDocument; if( m_document->id().isEmpty() ) { if( m_document->name().isEmpty() ) { m_document->setId(QStringLiteral("untitled_tour")); } else { m_document->setId( m_document->name().trimmed().replace( QLatin1Char(' '), QLatin1Char('_') ).toLower() ); } } document->setTargetId( m_document->id() ); GeoDataPlacemark *placemark = new GeoDataPlacemark; placemark->setCoordinate( lon, lat ); placemark->setVisible( true ); placemark->setBalloonVisible( true ); GeoDataStyle *newStyle = new GeoDataStyle( *placemark->style() ); newStyle->iconStyle().setIconPath(MarbleDirs::path(QStringLiteral("bitmaps/redflag_22.png"))); placemark->setStyle( GeoDataStyle::Ptr(newStyle) ); document->append( placemark ); GeoDataCreate *create = new GeoDataCreate; create->append( document ); GeoDataUpdate *update = new GeoDataUpdate; update->setCreate( create ); GeoDataAnimatedUpdate *animatedUpdate = new GeoDataAnimatedUpdate; animatedUpdate->setUpdate( update ); if( m_delegate->editAnimatedUpdate( animatedUpdate ) ) { addTourPrimitive( animatedUpdate ); m_delegate->setDefaultFeatureId( placemark->id() ); } else { delete animatedUpdate; } } void TourWidgetPrivate::addRemovePlacemark() { GeoDataDelete *deleteItem = new GeoDataDelete; GeoDataPlacemark *placemark = new GeoDataPlacemark; placemark->setTargetId( m_delegate->defaultFeatureId() ); deleteItem->append( placemark ); GeoDataUpdate *update = new GeoDataUpdate; update->setDelete( deleteItem ); GeoDataAnimatedUpdate *animatedUpdate = new GeoDataAnimatedUpdate; animatedUpdate->setUpdate( update ); addTourPrimitive( animatedUpdate ); } void TourWidgetPrivate::addChangePlacemark() { GeoDataChange *change = new GeoDataChange; GeoDataPlacemark *placemark = 0; GeoDataFeature *lastFeature = m_delegate->findFeature( m_delegate->defaultFeatureId() ); if( lastFeature != 0 && lastFeature->nodeType() == GeoDataTypes::GeoDataPlacemarkType ) { GeoDataPlacemark *target = static_cast( lastFeature ); placemark = new GeoDataPlacemark( *target ); placemark->setTargetId( m_delegate->defaultFeatureId() ); placemark->setId(QString()); } else { placemark = new GeoDataPlacemark; } change->append( placemark ); GeoDataUpdate *update = new GeoDataUpdate; update->setChange( change ); GeoDataAnimatedUpdate *animatedUpdate = new GeoDataAnimatedUpdate; animatedUpdate->setUpdate( update ); addTourPrimitive( animatedUpdate ); } void TourWidgetPrivate::addTourPrimitive( GeoDataTourPrimitive *primitive ) { GeoDataObject *rootObject = rootIndexObject(); if ( rootObject->nodeType() == GeoDataTypes::GeoDataPlaylistType ) { GeoDataPlaylist *playlist = static_cast( rootObject ); QModelIndex currentIndex = m_tourUi.m_listView->currentIndex(); QModelIndex playlistIndex = m_widget->model()->treeModel()->index( playlist ); int row = currentIndex.isValid() ? currentIndex.row()+1 : playlist->size(); m_widget->model()->treeModel()->addTourPrimitive( playlistIndex, primitive, row ); m_isChanged = true; m_tourUi.m_actionSaveTour->setEnabled( true ); // Scrolling to the inserted item. if ( currentIndex.isValid() ) { m_tourUi.m_listView->scrollTo( currentIndex ); } else { m_tourUi.m_listView->scrollToBottom(); } } } void TourWidgetPrivate::deleteSelected() { QString title = QObject::tr( "Remove Selected Items" ); QString text = QObject::tr( "Are you sure want to remove selected items?" ); QPointer dialog = new QMessageBox( QMessageBox::Question, title, text, QMessageBox::Yes | QMessageBox::No, q ); dialog->setDefaultButton( QMessageBox::No ); if ( dialog->exec() == QMessageBox::Yes ) { GeoDataObject *rootObject = rootIndexObject(); if ( rootObject && rootObject->nodeType() == GeoDataTypes::GeoDataPlaylistType ) { GeoDataPlaylist *playlist = static_cast( rootObject ); QModelIndex playlistIndex = m_widget->model()->treeModel()->index( playlist ); QModelIndexList selected = m_tourUi.m_listView->selectionModel()->selectedIndexes(); - std::sort( selected.begin(), selected.end(), qGreater() ); + std::sort( selected.begin(), selected.end(), [](const QModelIndex &a, const QModelIndex &b) { return b < a; } ); QModelIndexList::iterator end = selected.end(); QModelIndexList::iterator iter = selected.begin(); for( ; iter != end; ++iter ) { m_widget->model()->treeModel()->removeTourPrimitive( playlistIndex, iter->row() ); } m_isChanged = true; m_tourUi.m_actionSaveTour->setEnabled( true ); } } delete dialog; } void TourWidgetPrivate::updateButtonsStates() { QModelIndexList selectedIndexes = m_tourUi.m_listView->selectionModel()->selectedIndexes(); if ( selectedIndexes.isEmpty() ) { m_tourUi.m_actionDelete->setEnabled( false ); m_tourUi.m_actionMoveDown->setEnabled( false ); m_tourUi.m_actionMoveUp->setEnabled( false ); } else { m_tourUi.m_actionDelete->setEnabled( true ); - std::sort( selectedIndexes.begin(), selectedIndexes.end(), qLess() ); + std::sort( selectedIndexes.begin(), selectedIndexes.end(), std::less() ); QModelIndexList::iterator end = selectedIndexes.end()-1; QModelIndexList::iterator start = selectedIndexes.begin(); m_tourUi.m_actionMoveUp->setEnabled( ( start->row() != 0 ) ); // if we can move up enable action else disable. GeoDataObject *rootObject = rootIndexObject(); if ( rootObject && rootObject->nodeType() == GeoDataTypes::GeoDataPlaylistType ) { GeoDataPlaylist *playlist = static_cast( rootObject ); m_tourUi.m_actionMoveDown->setEnabled( ( end->row() != playlist->size()-1 ) ); // if we can move down enable action else disable. } } } void TourWidgetPrivate::moveUp() { GeoDataObject *rootObject = rootIndexObject(); if ( rootObject && rootObject->nodeType() == GeoDataTypes::GeoDataPlaylistType ) { GeoDataPlaylist *playlist = static_cast( rootObject ); QModelIndex playlistIndex = m_widget->model()->treeModel()->index( playlist ); QModelIndexList selected = m_tourUi.m_listView->selectionModel()->selectedIndexes(); - std::sort( selected.begin(), selected.end(), qLess() ); + std::sort( selected.begin(), selected.end(), std::less() ); QModelIndexList::iterator end = selected.end(); QModelIndexList::iterator iter = selected.begin(); for( ; iter != end; ++iter ) { int const index = iter->row(); Q_ASSERT( index > 0 ); m_widget->model()->treeModel()->swapTourPrimitives( playlistIndex, index-1, index ); } m_isChanged = true; m_tourUi.m_actionSaveTour->setEnabled( true ); updateButtonsStates(); } } void TourWidgetPrivate::moveDown() { GeoDataObject *rootObject = rootIndexObject(); if ( rootObject && rootObject->nodeType() == GeoDataTypes::GeoDataPlaylistType ) { GeoDataPlaylist *playlist = static_cast( rootObject ); QModelIndex playlistIndex = m_widget->model()->treeModel()->index( playlist ); QModelIndexList selected = m_tourUi.m_listView->selectionModel()->selectedIndexes(); - std::sort( selected.begin(), selected.end(), qGreater() ); + std::sort( selected.begin(), selected.end(), [](const QModelIndex &a, const QModelIndex &b) { return b < a; } ); QModelIndexList::iterator end = selected.end(); QModelIndexList::iterator iter = selected.begin(); for( ; iter != end; ++iter ) { int const index = iter->row(); Q_ASSERT( index < playlist->size()-1 ); m_widget->model()->treeModel()->swapTourPrimitives( playlistIndex, index, index+1 ); } m_isChanged = true; m_tourUi.m_actionSaveTour->setEnabled( true ); updateButtonsStates(); } } GeoDataFeature* TourWidgetPrivate::getPlaylistFeature() const { GeoDataObject *rootObject = rootIndexObject(); if ( rootObject && rootObject->nodeType() == GeoDataTypes::GeoDataPlaylistType ) { GeoDataPlaylist *playlist = static_cast( rootObject ); GeoDataObject *object = playlist->parent(); if ( object && object->nodeType() == GeoDataTypes::GeoDataTourType ) { return static_cast( object ); } } return 0; } void TourWidgetPrivate::updateRootIndex() { GeoDataTour *tour = findTour( m_document ); if ( tour ){ GeoDataPlaylist *playlist = tour->playlist(); if ( playlist ) { m_tourUi.m_listView->setModel( m_widget->model()->treeModel() ); m_tourUi.m_listView->setRootIndex( m_widget->model()->treeModel()->index( playlist ) ); QObject::connect( m_tourUi.m_listView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(updateButtonsStates()) ); } m_playback.setMarbleWidget( m_widget ); m_playback.setTour( tour ); m_tourUi.m_slider->setMaximum( m_playback.duration() * 100 ); QTime nullTime( 0, 0, 0 ); QTime time = nullTime.addSecs( m_playback.duration() ); m_tourUi.m_totalTime->setText(QString("%L1:%L2").arg(time.minute(), 2, 10, QLatin1Char('0')).arg(time.second(), 2, 10, QLatin1Char('0'))); QObject::connect( &m_playback, SIGNAL(progressChanged(double)), q, SLOT(handlePlaybackProgress(double)) ); q->stopPlaying(); m_tourUi.m_toolBarPlayback->setEnabled( true ); bool isPlaybackEmpty = m_playback.mainTrackSize() != 0; m_tourUi.actionPlay->setEnabled( isPlaybackEmpty ); m_tourUi.m_slider->setEnabled( isPlaybackEmpty ); m_tourUi.m_actionRecord->setEnabled( isPlaybackEmpty ); m_tourUi.actionStop->setEnabled( false ); if( m_playback.mainTrackSize() > 0 ) { if( dynamic_cast( m_playback.mainTrackItemAt( 0 ) ) ) { QModelIndex playlistIndex = m_widget->model()->treeModel()->index( playlist ); for( int i = 0; playlist && i < playlist->size(); ++i ) { if( playlist->primitive( i )->nodeType() == GeoDataTypes::GeoDataFlyToType ) { m_delegate->setFirstFlyTo( m_widget->model()->treeModel()->index( i, 0, playlistIndex ) ); break; } } } else { m_delegate->setFirstFlyTo( QPersistentModelIndex() ); } } } } void TourWidget::addFlyTo() { d->addFlyTo(); finishAddingItem(); } void TourWidget::addWait() { d->addWait(); finishAddingItem(); } void TourWidget::addSoundCue() { d->addSoundCue(); finishAddingItem(); } void TourWidget::addPlacemark() { d->addPlacemark(); finishAddingItem(); } void TourWidget::addRemovePlacemark() { d->addRemovePlacemark(); finishAddingItem(); } void TourWidget::addChangePlacemark() { d->addChangePlacemark(); finishAddingItem(); } void TourWidget::deleteSelected() { d->deleteSelected(); GeoDataFeature *feature = d->getPlaylistFeature(); if ( feature ) { emit featureUpdated( feature ); d->updateRootIndex(); } } void TourWidget::updateDuration() { d->m_tourUi.m_slider->setMaximum( d->m_playback.duration() * 100 ); QTime nullTime( 0, 0, 0 ); QTime totalTime = nullTime.addSecs( d->m_playback.duration() ); d->m_tourUi.m_totalTime->setText(QString("%L1:%L2").arg(totalTime.minute(), 2, 10, QLatin1Char('0') ).arg(totalTime.second(), 2, 10, QLatin1Char('0'))); d->m_tourUi.m_slider->setValue( 0 ); d->m_tourUi.m_elapsedTime->setText(QString("%L1:%L2").arg(0, 2, 10, QLatin1Char('0')).arg(0, 2, 10, QLatin1Char('0'))); } void TourWidget::finishAddingItem() { GeoDataFeature *feature = d->getPlaylistFeature(); if ( feature ) { emit featureUpdated( feature ); d->updateRootIndex(); } } void TourWidget::moveDown() { d->moveDown(); GeoDataFeature *feature = d->getPlaylistFeature(); if ( feature ) { emit featureUpdated( feature ); d->updateRootIndex(); } } void TourWidget::moveUp() { d->moveUp(); GeoDataFeature *feature = d->getPlaylistFeature(); if ( feature ) { emit featureUpdated( feature ); d->updateRootIndex(); } } GeoDataObject *TourWidgetPrivate::rootIndexObject() const { QModelIndex const rootIndex = m_tourUi.m_listView->rootIndex(); return rootIndex.isValid() ? static_cast( rootIndex.internalPointer() ) : 0; } void TourWidgetPrivate::createTour() { if ( overrideModifications() ) { GeoDataDocument *document = new GeoDataDocument(); document->setDocumentRole( UserDocument ); document->setName(QStringLiteral("New Tour")); document->setId(QStringLiteral("new_tour")); GeoDataTour *tour = new GeoDataTour(); tour->setName(QStringLiteral("New Tour")); GeoDataPlaylist *playlist = new GeoDataPlaylist; tour->setPlaylist( playlist ); document->append( static_cast( tour ) ); m_playback.setBaseUrl( QUrl::fromLocalFile( MarbleDirs::marbleDataPath() ) ); openDocument( document ); m_isChanged = true; m_tourUi.m_actionSaveTour->setEnabled( true ); m_tourUi.m_slider->setEnabled( true ); } } bool TourWidgetPrivate::openDocument(GeoDataDocument* document) { if ( document ) { if ( m_document ) { m_widget->model()->treeModel()->removeDocument( m_document ); delete m_document; } m_document = document; m_widget->model()->treeModel()->addDocument( m_document ); m_isChanged = false; updateRootIndex(); m_addPrimitiveButton->setEnabled( true ); m_tourUi.m_actionSaveTourAs->setEnabled( true ); m_tourUi.m_actionSaveTour->setEnabled( false ); m_isChanged = false; return true; } return false; } void TourWidgetPrivate::saveTour() { if ( m_document ) { if ( !m_document->fileName().isEmpty() ) { saveTourAs( m_document->fileName() ); } else { saveTourAs(); } } } void TourWidgetPrivate::saveTourAs() { if ( m_document ) { QString const filename = QFileDialog::getSaveFileName( q, QObject::tr( "Save Tour as" ), QDir::homePath(), QObject::tr( "KML Tours (*.kml)" ) ); if ( !filename.isEmpty() ) { saveTourAs( filename ); } } } bool TourWidgetPrivate::saveTourAs(const QString &filename) { if ( !filename.isEmpty() ) { if (GeoDataDocumentWriter::write(filename, *m_document)) { m_tourUi.m_actionSaveTour->setEnabled( false ); m_isChanged = false; GeoDataDocument* document = m_document; if ( !document->fileName().isNull() ) { m_widget->model()->removeGeoData( document->fileName() ); } m_widget->model()->addGeoDataFile( filename ); m_document->setFileName( filename ); return true; } } return false; } void TourWidgetPrivate::captureTour() { MarbleWidget* widget = new MarbleWidget; widget->setMapThemeId( m_widget->mapThemeId() ); widget->resize( 1280, 720 ); m_widget->model()->treeModel()->removeDocument(m_document); widget->model()->treeModel()->addDocument(m_document); GeoDataTour* tour = findTour( m_document ); TourPlayback* playback = new TourPlayback; playback->setMarbleWidget( widget ); playback->setTour( tour ); m_tourUi.m_listView->setModel( widget->model()->treeModel() ); if( tour ){ m_tourUi.m_listView->setRootIndex( widget->model()->treeModel()->index( tour->playlist() ) ); m_tourUi.m_listView->repaint(); QPointer tourCaptureDialog = new TourCaptureDialog( widget, m_widget ); tourCaptureDialog->setDefaultFilename( tour->name() ); tourCaptureDialog->setTourPlayback( playback ); tourCaptureDialog->exec(); } delete playback; widget->model()->treeModel()->removeDocument(m_document); m_widget->model()->treeModel()->addDocument(m_document); updateRootIndex(); delete widget; } bool TourWidgetPrivate::overrideModifications() { if ( m_document && m_isChanged ) { QString title = QObject::tr( "Discard Changes" ); QString text = QObject::tr( "Are you sure want to discard all unsaved changes and close current document?" ); QPointer dialog = new QMessageBox( QMessageBox::Question, title, text, QMessageBox::Yes | QMessageBox::No, q ); dialog->setDefaultButton( QMessageBox::No ); if ( dialog->exec() != QMessageBox::Yes ) { delete dialog; return false; } delete dialog; } return true; } bool TourWidget::openTour( const QString &filename) { return d->openFile( filename ); } void TourWidgetPrivate::handlePlaybackProgress(const double position) { if( !m_tourUi.m_slider->isSliderDown() ){ m_tourUi.m_slider->setValue( position * 100 ); QTime nullTime( 0, 0, 0 ); QTime time = nullTime.addSecs( position ); m_tourUi.m_elapsedTime->setText(QString("%L1:%L2").arg(time.minute(), 2, 10, QLatin1Char('0')).arg(time.second(), 2, 10, QLatin1Char('0'))); } } void TourWidget::setHighlightedItemIndex( int index ) { GeoDataObject* rootObject = d->rootIndexObject(); GeoDataPlaylist* playlist = static_cast( rootObject ); QModelIndex playlistIndex = d->m_widget->model()->treeModel()->index( playlist ); // Only flyTo and wait items have duration, so the other types have to be skipped. int searchedIndex = 0; for ( int i = 0; i < playlist->size(); i++ ) { QModelIndex currentIndex = d->m_widget->model()->treeModel()->index( i, 0, playlistIndex ); GeoDataObject* object = qvariant_cast(currentIndex.data( MarblePlacemarkModel::ObjectPointerRole ) ); if ( object->nodeType() == GeoDataTypes::GeoDataFlyToType || object->nodeType() == GeoDataTypes::GeoDataWaitType ) ++searchedIndex; if ( index == searchedIndex ) { d->m_tourUi.m_listView->selectionModel()->setCurrentIndex( currentIndex, QItemSelectionModel::NoUpdate ); d->m_tourUi.m_listView->scrollTo( currentIndex ); break; } } d->m_tourUi.m_listView->viewport()->update(); } void TourWidget::removeHighlight() { QModelIndex index; // Restoring the CurrentIndex to the previously selected item // or clearing it if there was no selected item. if ( d->m_tourUi.m_listView->selectionModel()->hasSelection() ) { index = d->m_tourUi.m_listView->selectionModel()->selectedIndexes().last(); } else { index = QModelIndex(); } d->m_tourUi.m_listView->selectionModel()->setCurrentIndex( index, QItemSelectionModel::NoUpdate ); d->m_tourUi.m_listView->viewport()->update(); } bool TourWidget::isPlaying() const { return d->m_playState; } } #include "moc_TourWidget.cpp" diff --git a/src/lib/marble/layers/TextureLayer.cpp b/src/lib/marble/layers/TextureLayer.cpp index 8965cb84d..2c3a7ca72 100644 --- a/src/lib/marble/layers/TextureLayer.cpp +++ b/src/lib/marble/layers/TextureLayer.cpp @@ -1,589 +1,589 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2006-2007 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2008, 2009, 2010 Jens-Michael Hoffmann // Copyright 2010-2012 Bernhard Beschow // #include "TextureLayer.h" #include #include #include #include #include "SphericalScanlineTextureMapper.h" #include "EquirectScanlineTextureMapper.h" #include "MercatorScanlineTextureMapper.h" #include "GenericScanlineTextureMapper.h" #include "TileScalingTextureMapper.h" #include "GeoDataGroundOverlay.h" #include "GeoPainter.h" #include "GeoSceneGroup.h" #include "GeoSceneTextureTileDataset.h" #include "GeoSceneTypes.h" #include "MergedLayerDecorator.h" #include "MarbleDebug.h" #include "MarbleDirs.h" #include "MarblePlacemarkModel.h" #include "StackedTile.h" #include "StackedTileLoader.h" #include "SunLocator.h" #include "TextureColorizer.h" #include "TileLoader.h" #include "ViewportParams.h" namespace Marble { const int REPAINT_SCHEDULING_INTERVAL = 1000; class Q_DECL_HIDDEN TextureLayer::Private { public: Private( HttpDownloadManager *downloadManager, PluginManager* pluginManager, const SunLocator *sunLocator, QAbstractItemModel *groundOverlayModel, TextureLayer *parent ); void requestDelayedRepaint(); void updateTextureLayers(); void updateTile( const TileId &tileId, const QImage &tileImage ); void addGroundOverlays( const QModelIndex& parent, int first, int last ); void removeGroundOverlays( const QModelIndex& parent, int first, int last ); void resetGroundOverlaysCache(); void updateGroundOverlays(); void addCustomTextures(); static bool drawOrderLessThan( const GeoDataGroundOverlay* o1, const GeoDataGroundOverlay* o2 ); public: TextureLayer *const m_parent; const SunLocator *const m_sunLocator; TileLoader m_loader; MergedLayerDecorator m_layerDecorator; StackedTileLoader m_tileLoader; GeoDataCoordinates m_centerCoordinates; int m_tileZoomLevel; TextureMapperInterface *m_texmapper; TextureColorizer *m_texcolorizer; QVector m_textures; const GeoSceneGroup *m_textureLayerSettings; QString m_runtimeTrace; QSortFilterProxyModel m_groundOverlayModel; QList m_groundOverlayCache; QMap m_customTextures; // For scheduling repaints QTimer m_repaintTimer; RenderState m_renderState; }; TextureLayer::Private::Private( HttpDownloadManager *downloadManager, PluginManager* pluginManager, const SunLocator *sunLocator, QAbstractItemModel *groundOverlayModel, TextureLayer *parent ) : m_parent( parent ) , m_sunLocator( sunLocator ) , m_loader( downloadManager, pluginManager ) , m_layerDecorator( &m_loader, sunLocator ) , m_tileLoader( &m_layerDecorator ) , m_centerCoordinates() , m_tileZoomLevel( -1 ) , m_texmapper( 0 ) , m_texcolorizer( 0 ) , m_textureLayerSettings( 0 ) , m_repaintTimer() { m_groundOverlayModel.setSourceModel( groundOverlayModel ); m_groundOverlayModel.setDynamicSortFilter( true ); m_groundOverlayModel.setSortRole ( MarblePlacemarkModel::PopularityIndexRole ); m_groundOverlayModel.sort (0, Qt::AscendingOrder ); connect( &m_groundOverlayModel, SIGNAL(rowsInserted(QModelIndex,int,int)), m_parent, SLOT(addGroundOverlays(QModelIndex,int,int)) ); connect( &m_groundOverlayModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), m_parent, SLOT(removeGroundOverlays(QModelIndex,int,int)) ); connect( &m_groundOverlayModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), m_parent, SLOT(resetGroundOverlaysCache()) ); connect( &m_groundOverlayModel, SIGNAL(modelReset()), m_parent, SLOT(resetGroundOverlaysCache()) ); updateGroundOverlays(); } void TextureLayer::Private::requestDelayedRepaint() { if ( m_texmapper ) { m_texmapper->setRepaintNeeded(); } if ( !m_repaintTimer.isActive() ) { m_repaintTimer.start(); } } void TextureLayer::Private::updateTextureLayers() { QVector result; foreach ( const GeoSceneTextureTileDataset *candidate, m_textures ) { bool enabled = true; if ( m_textureLayerSettings ) { const bool propertyExists = m_textureLayerSettings->propertyValue( candidate->name(), enabled ); enabled |= !propertyExists; // if property doesn't exist, enable texture nevertheless } if ( enabled ) { result.append( candidate ); mDebug() << "enabling texture" << candidate->name(); } else { mDebug() << "disabling texture" << candidate->name(); } } updateGroundOverlays(); m_layerDecorator.setTextureLayers( result ); m_tileLoader.clear(); m_tileZoomLevel = -1; m_parent->setNeedsUpdate(); } void TextureLayer::Private::updateTile( const TileId &tileId, const QImage &tileImage ) { if ( tileImage.isNull() ) return; // keep tiles in cache to improve performance m_tileLoader.updateTile( tileId, tileImage ); requestDelayedRepaint(); } bool TextureLayer::Private::drawOrderLessThan( const GeoDataGroundOverlay* o1, const GeoDataGroundOverlay* o2 ) { return o1->drawOrder() < o2->drawOrder(); } void TextureLayer::Private::addGroundOverlays( const QModelIndex& parent, int first, int last ) { for ( int i = first; i <= last; ++i ) { QModelIndex index = m_groundOverlayModel.index( i, 0, parent ); const GeoDataGroundOverlay *overlay = static_cast( qvariant_cast( index.data( MarblePlacemarkModel::ObjectPointerRole ) ) ); if ( overlay->icon().isNull() ) { continue; } - int pos = qLowerBound( m_groundOverlayCache.begin(), m_groundOverlayCache.end(), overlay, drawOrderLessThan ) - m_groundOverlayCache.begin(); + int pos = std::lower_bound( m_groundOverlayCache.begin(), m_groundOverlayCache.end(), overlay, drawOrderLessThan ) - m_groundOverlayCache.begin(); m_groundOverlayCache.insert( pos, overlay ); } updateGroundOverlays(); m_parent->reset(); } void TextureLayer::Private::removeGroundOverlays( const QModelIndex& parent, int first, int last ) { for ( int i = first; i <= last; ++i ) { QModelIndex index = m_groundOverlayModel.index( i, 0, parent ); const GeoDataGroundOverlay *overlay = static_cast( qvariant_cast( index.data( MarblePlacemarkModel::ObjectPointerRole ) ) ); - int pos = qLowerBound( m_groundOverlayCache.begin(), m_groundOverlayCache.end(), overlay, drawOrderLessThan ) - m_groundOverlayCache.begin(); + int pos = std::lower_bound( m_groundOverlayCache.begin(), m_groundOverlayCache.end(), overlay, drawOrderLessThan ) - m_groundOverlayCache.begin(); if (pos >= 0 && pos < m_groundOverlayCache.size() ) { m_groundOverlayCache.removeAt( pos ); } } updateGroundOverlays(); m_parent->reset(); } void TextureLayer::Private::resetGroundOverlaysCache() { m_groundOverlayCache.clear(); updateGroundOverlays(); m_parent->reset(); } void TextureLayer::Private::updateGroundOverlays() { if ( !m_texcolorizer ) { m_layerDecorator.updateGroundOverlays( m_groundOverlayCache ); } else { m_layerDecorator.updateGroundOverlays( QList() ); } } void TextureLayer::Private::addCustomTextures() { m_textures.reserve(m_textures.size() + m_customTextures.size()); foreach (GeoSceneTextureTileDataset *t, m_customTextures) { m_textures.append(t); } } TextureLayer::TextureLayer( HttpDownloadManager *downloadManager, PluginManager* pluginManager, const SunLocator *sunLocator, QAbstractItemModel *groundOverlayModel ) : QObject() , d( new Private( downloadManager, pluginManager, sunLocator, groundOverlayModel, this ) ) { connect( &d->m_loader, SIGNAL(tileCompleted(TileId,QImage)), this, SLOT(updateTile(TileId,QImage)) ); // Repaint timer d->m_repaintTimer.setSingleShot( true ); d->m_repaintTimer.setInterval( REPAINT_SCHEDULING_INTERVAL ); connect( &d->m_repaintTimer, SIGNAL(timeout()), this, SIGNAL(repaintNeeded()) ); } TextureLayer::~TextureLayer() { qDeleteAll(d->m_customTextures); delete d->m_texmapper; delete d->m_texcolorizer; delete d; } QStringList TextureLayer::renderPosition() const { return QStringList(QStringLiteral("SURFACE")); } void TextureLayer::addSeaDocument( const GeoDataDocument *seaDocument ) { if( d->m_texcolorizer ) { d->m_texcolorizer->addSeaDocument( seaDocument ); reset(); } } void TextureLayer::addLandDocument( const GeoDataDocument *landDocument ) { if( d->m_texcolorizer ) { d->m_texcolorizer->addLandDocument( landDocument ); reset(); } } int TextureLayer::textureLayerCount() const { return d->m_layerDecorator.textureLayersSize(); } bool TextureLayer::showSunShading() const { return d->m_layerDecorator.showSunShading(); } bool TextureLayer::showCityLights() const { return d->m_layerDecorator.showCityLights(); } bool TextureLayer::render( GeoPainter *painter, ViewportParams *viewport, const QString &renderPos, GeoSceneLayer *layer ) { Q_UNUSED( renderPos ); Q_UNUSED( layer ); d->m_runtimeTrace = QStringLiteral("Texture Cache: %1 ").arg(d->m_tileLoader.tileCount()); d->m_renderState = RenderState(QStringLiteral("Texture Tiles")); // Stop repaint timer if it is already running d->m_repaintTimer.stop(); if ( d->m_textures.isEmpty() ) return false; if ( d->m_layerDecorator.textureLayersSize() == 0 ) return false; if ( !d->m_texmapper ) return false; if ( d->m_centerCoordinates.longitude() != viewport->centerLongitude() || d->m_centerCoordinates.latitude() != viewport->centerLatitude() ) { d->m_centerCoordinates.setLongitude( viewport->centerLongitude() ); d->m_centerCoordinates.setLatitude( viewport->centerLatitude() ); d->m_texmapper->setRepaintNeeded(); } // choose the smaller dimension for selecting the tile level, leading to higher-resolution results const int levelZeroWidth = d->m_layerDecorator.tileSize().width() * d->m_layerDecorator.tileColumnCount( 0 ); const int levelZeroHight = d->m_layerDecorator.tileSize().height() * d->m_layerDecorator.tileRowCount( 0 ); const int levelZeroMinDimension = qMin( levelZeroWidth, levelZeroHight ); // limit to 1 as dirty fix for invalid entry linearLevel const qreal linearLevel = qMax( 1.0, viewport->radius() * 4.0 / levelZeroMinDimension ); // As our tile resolution doubles with each level we calculate // the tile level from tilesize and the globe radius via log(2) const qreal tileLevelF = qLn( linearLevel ) / qLn( 2.0 ) * 1.00001; // snap to the sharper tile level a tiny bit earlier // to work around rounding errors when the radius // roughly equals the global texture width const int tileLevel = qMin( d->m_layerDecorator.maximumTileLevel(), tileLevelF ); if ( tileLevel != d->m_tileZoomLevel ) { d->m_tileZoomLevel = tileLevel; emit tileLevelChanged( d->m_tileZoomLevel ); } const QRect dirtyRect = QRect( QPoint( 0, 0), viewport->size() ); d->m_texmapper->mapTexture( painter, viewport, d->m_tileZoomLevel, dirtyRect, d->m_texcolorizer ); d->m_renderState.addChild( d->m_tileLoader.renderState() ); return true; } QString TextureLayer::runtimeTrace() const { return d->m_runtimeTrace; } void TextureLayer::setShowRelief( bool show ) { if ( d->m_texcolorizer ) { d->m_texcolorizer->setShowRelief( show ); } } void TextureLayer::setShowSunShading( bool show ) { disconnect( d->m_sunLocator, SIGNAL(positionChanged(qreal,qreal)), this, SLOT(reset()) ); if ( show ) { connect( d->m_sunLocator, SIGNAL(positionChanged(qreal,qreal)), this, SLOT(reset()) ); } d->m_layerDecorator.setShowSunShading( show ); reset(); } void TextureLayer::setShowCityLights( bool show ) { d->m_layerDecorator.setShowCityLights( show ); reset(); } void TextureLayer::setShowTileId( bool show ) { d->m_layerDecorator.setShowTileId( show ); reset(); } void TextureLayer::setProjection( Projection projection ) { if ( d->m_textures.isEmpty() ) { return; } // FIXME: replace this with an approach based on the factory method pattern. delete d->m_texmapper; switch( projection ) { case Spherical: d->m_texmapper = new SphericalScanlineTextureMapper( &d->m_tileLoader ); break; case Equirectangular: d->m_texmapper = new EquirectScanlineTextureMapper( &d->m_tileLoader ); break; case Mercator: if (d->m_textures.at(0)->tileProjectionType() == GeoSceneAbstractTileProjection::Mercator) { d->m_texmapper = new TileScalingTextureMapper( &d->m_tileLoader ); } else { d->m_texmapper = new MercatorScanlineTextureMapper( &d->m_tileLoader ); } break; case Gnomonic: case Stereographic: case LambertAzimuthal: case AzimuthalEquidistant: case VerticalPerspective: d->m_texmapper = new GenericScanlineTextureMapper( &d->m_tileLoader ); break; default: d->m_texmapper = 0; } Q_ASSERT( d->m_texmapper ); } void TextureLayer::setNeedsUpdate() { if ( d->m_texmapper ) { d->m_texmapper->setRepaintNeeded(); } emit repaintNeeded(); } void TextureLayer::setVolatileCacheLimit( quint64 kilobytes ) { d->m_tileLoader.setVolatileCacheLimit( kilobytes ); } void TextureLayer::reset() { d->m_tileLoader.clear(); setNeedsUpdate(); } void TextureLayer::reload() { foreach ( const TileId &id, d->m_tileLoader.visibleTiles() ) { // it's debatable here, whether DownloadBulk or DownloadBrowse should be used // but since "reload" or "refresh" seems to be a common action of a browser and it // allows for more connections (in our model), use "DownloadBrowse" d->m_layerDecorator.downloadStackedTile( id, DownloadBrowse ); } } void TextureLayer::downloadStackedTile( const TileId &stackedTileId ) { d->m_layerDecorator.downloadStackedTile( stackedTileId, DownloadBulk ); } void TextureLayer::setMapTheme( const QVector &textures, const GeoSceneGroup *textureLayerSettings, const QString &seaFile, const QString &landFile ) { delete d->m_texcolorizer; d->m_texcolorizer = 0; if ( QFileInfo( seaFile ).isReadable() || QFileInfo( landFile ).isReadable() ) { d->m_texcolorizer = new TextureColorizer( seaFile, landFile ); } d->m_textures = textures; d->addCustomTextures(); d->m_textureLayerSettings = textureLayerSettings; if ( d->m_textureLayerSettings ) { connect( d->m_textureLayerSettings, SIGNAL(valueChanged(QString,bool)), this, SLOT(updateTextureLayers()) ); } d->updateTextureLayers(); } int TextureLayer::tileZoomLevel() const { return d->m_tileZoomLevel; } QSize TextureLayer::tileSize() const { return d->m_layerDecorator.tileSize(); } const GeoSceneAbstractTileProjection *TextureLayer::tileProjection() const { return d->m_layerDecorator.tileProjection(); } int TextureLayer::tileColumnCount( int level ) const { return d->m_layerDecorator.tileColumnCount( level ); } int TextureLayer::tileRowCount( int level ) const { return d->m_layerDecorator.tileRowCount( level ); } quint64 TextureLayer::volatileCacheLimit() const { return d->m_tileLoader.volatileCacheLimit(); } int TextureLayer::preferredRadiusCeil( int radius ) const { if (!d->m_layerDecorator.hasTextureLayer()) { return radius; } const int tileWidth = d->m_layerDecorator.tileSize().width(); const int levelZeroColumns = d->m_layerDecorator.tileColumnCount( 0 ); const qreal linearLevel = 4.0 * (qreal)( radius ) / (qreal)( tileWidth * levelZeroColumns ); const qreal tileLevelF = qLn( linearLevel ) / qLn( 2.0 ); const int tileLevel = qCeil( tileLevelF ); if ( tileLevel < 0 ) return ( tileWidth * levelZeroColumns / 4 ) >> (-tileLevel); return ( tileWidth * levelZeroColumns / 4 ) << tileLevel; } int TextureLayer::preferredRadiusFloor( int radius ) const { if (!d->m_layerDecorator.hasTextureLayer()) { return radius; } const int tileWidth = d->m_layerDecorator.tileSize().width(); const int levelZeroColumns = d->m_layerDecorator.tileColumnCount( 0 ); const qreal linearLevel = 4.0 * (qreal)( radius ) / (qreal)( tileWidth * levelZeroColumns ); const qreal tileLevelF = qLn( linearLevel ) / qLn( 2.0 ); const int tileLevel = qFloor( tileLevelF ); if ( tileLevel < 0 ) return ( tileWidth * levelZeroColumns / 4 ) >> (-tileLevel); return ( tileWidth * levelZeroColumns / 4 ) << tileLevel; } RenderState TextureLayer::renderState() const { return d->m_renderState; } QString TextureLayer::addTextureLayer(GeoSceneTextureTileDataset* texture) { if (!texture) return QString(); //Not a sane call QString sourceDir = texture->sourceDir(); if (!d->m_customTextures.contains(sourceDir)) { // Add if not present. For update, remove the old texture first. d->m_customTextures.insert(sourceDir, texture); d->m_textures.append(texture); d->updateTextureLayers(); } return sourceDir; } void TextureLayer::removeTextureLayer(const QString &key) { if (d->m_customTextures.contains(key)) { GeoSceneTextureTileDataset *texture = d->m_customTextures.value(key); d->m_customTextures.remove(key); d->m_textures.remove(d->m_textures.indexOf(texture)); delete texture; d->updateTextureLayers(); } } } #include "moc_TextureLayer.cpp" diff --git a/src/plugins/render/eclipses/EclipsesPlugin.h b/src/plugins/render/eclipses/EclipsesPlugin.h index b69142628..209b95694 100644 --- a/src/plugins/render/eclipses/EclipsesPlugin.h +++ b/src/plugins/render/eclipses/EclipsesPlugin.h @@ -1,154 +1,156 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2012 Rene Kuettner // #ifndef MARBLE_ECLIPSESPLUGIN_H #define MARBLE_ECLIPSESPLUGIN_H #include "RenderPlugin.h" #include "DialogConfigurationInterface.h" +#include + class QMenu; namespace Ui { class EclipsesConfigDialog; class EclipsesReminderDialog; } namespace Marble { class MarbleWidget; class EclipsesModel; class EclipsesItem; class EclipsesBrowserDialog; /** * @brief This plugin displays solar eclipses. * * It utilizes Gerhard Holtcamps eclsolar class to render nice * visualizations of eclipse events on earth. */ class EclipsesPlugin : public RenderPlugin, public DialogConfigurationInterface { Q_OBJECT Q_PLUGIN_METADATA(IID "org.kde.marble.EclipsesPlugin") Q_INTERFACES( Marble::RenderPluginInterface ) Q_INTERFACES( Marble::DialogConfigurationInterface ) MARBLE_PLUGIN( EclipsesPlugin ) public: EclipsesPlugin(); explicit EclipsesPlugin( const MarbleModel *marbleModel ); ~EclipsesPlugin() override; // this is the implementation of the RenderPlugin interface // see RenderPlugin.h for a description QStringList backendTypes() const override; QString renderPolicy() const override; QStringList renderPosition() const override; QString name() const override; QString nameId() const override; QString guiString() const override; QString version() const override; QString description() const override; QString copyrightYears() const override; QVector pluginAuthors() const override; QIcon icon() const override; RenderPlugin::RenderType renderType() const override; QList* actionGroups() const override; QDialog *configDialog() override; void initialize() override; bool isInitialized() const override; bool render( GeoPainter *painter, ViewportParams *viewport, const QString &renderPos, GeoSceneLayer *layer ) override; QHash settings() const override; void setSettings( const QHash &settings ) override; protected: bool eventFilter( QObject *object, QEvent *e ) override; private Q_SLOTS: void readSettings(); void writeSettings(); void updateSettings(); /** * @brief Update list of eclipses for the current year * * This calculates the list of eclipses for the year the marble clock * is set to. */ void updateEclipses(); /** * @brief Show an eclipse event on the marble map * * @param year The year the eclipse event happens * @param index The index of the eclipse in this year * * Shows the eclipse with index @p index in year @p year by setting * the marble clock to the time of the eclipse's maximum. */ void showEclipse( int year, int index ); /** * @brief Show an eclipse event selected from the menu * * @param action The menu items action * * Shows the eclipse the menu item given by @p action refers to. * The eclipse's index is stored in the actions data field while the * year is taken from the action's text. */ void showEclipseFromMenu( QAction *action ); /** * @brief Update menu item state * * Updates the state of the plugin's menu items. They will be disabled for * non earth themes since we only support eclipse events on earth. */ void updateMenuItemState(); private: bool renderItem( GeoPainter *painter, EclipsesItem *item ) const; private: bool m_isInitialized; MarbleWidget *m_marbleWidget; EclipsesModel *m_model; QList m_actionGroups; QActionGroup *m_eclipsesActionGroup; QHash m_settings; QAction *m_eclipsesMenuAction; QMenu *m_eclipsesListMenu; int m_menuYear; // dialogs QDialog *m_configDialog; Ui::EclipsesConfigDialog *m_configWidget; EclipsesBrowserDialog *m_browserDialog; QDialog *m_reminderDialog; Ui::EclipsesReminderDialog *m_reminderWidget; }; } #endif // MARBLE_ECLIPSESPLUGIN_H diff --git a/src/plugins/render/satellites/SatellitesPlugin.h b/src/plugins/render/satellites/SatellitesPlugin.h index 0bc061a1f..160c1aca8 100644 --- a/src/plugins/render/satellites/SatellitesPlugin.h +++ b/src/plugins/render/satellites/SatellitesPlugin.h @@ -1,105 +1,107 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2011 Guillaume Martres // Copyright 2012 Rene Kuettner // #ifndef MARBLE_SATELLITESPLUGIN_H #define MARBLE_SATELLITESPLUGIN_H #include "RenderPlugin.h" #include "SatellitesConfigDialog.h" #include "DialogConfigurationInterface.h" #include "SatellitesModel.h" +#include + namespace Marble { class SatellitesConfigModel; /** * @brief This plugin displays satellites and their orbits. * */ class SatellitesPlugin : public RenderPlugin, public DialogConfigurationInterface { Q_OBJECT Q_PLUGIN_METADATA(IID "org.kde.marble.SatellitesPlugin") Q_INTERFACES( Marble::RenderPluginInterface ) Q_INTERFACES( Marble::DialogConfigurationInterface ) MARBLE_PLUGIN( SatellitesPlugin ) public: explicit SatellitesPlugin( const MarbleModel *marbleModel = 0 ); ~SatellitesPlugin() override; QStringList backendTypes() const override; QString renderPolicy() const override; QStringList renderPosition() const override; QString name() const override; QString nameId() const override; QString guiString() const override; QString version() const override; QString description() const override; QString copyrightYears() const override; QVector pluginAuthors() const override; QString aboutDataText() const override; QIcon icon() const override; RenderType renderType() const override; void initialize() override; bool isInitialized() const override; bool render( GeoPainter *painter, ViewportParams *viewport, const QString &renderPos, GeoSceneLayer *layer ) override; bool eventFilter( QObject *object, QEvent *event ) override; QHash settings() const override; void setSettings( const QHash &settings ) override; SatellitesConfigDialog *configDialog() override; private Q_SLOTS: void activate(); void enableModel( bool enabled ); void visibleModel( bool visible ); void readSettings(); void writeSettings(); void updateSettings(); void updateDataSourceConfig( const QString &source ); void dataSourceParsed( const QString &source ); void userDataSourceAdded( const QString &source ); void showOrbit( bool show ); void trackPlacemark(); protected: void activateDataSource( const QString &source ); void addBuiltInDataSources(); private: SatellitesModel *m_satModel; SatellitesConfigModel *m_configModel; bool m_isInitialized; QHash m_settings; QStringList m_newDataSources; SatellitesConfigDialog *m_configDialog; QAction *m_showOrbitAction; QAction *m_trackPlacemarkAction; QVector m_trackerList; }; } // namespace Marble #endif // MARBLE_SATELLITESPLUGIN_H diff --git a/src/plugins/render/weather/StationListParser.cpp b/src/plugins/render/weather/StationListParser.cpp index dc8f2eb2a..66e143357 100644 --- a/src/plugins/render/weather/StationListParser.cpp +++ b/src/plugins/render/weather/StationListParser.cpp @@ -1,197 +1,197 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2009 Bastian Holst // // Self #include "StationListParser.h" // Marble #include "MarbleGlobal.h" #include "BBCStation.h" #include "GeoDataCoordinates.h" #include "MarbleDebug.h" // Qt #include #include using namespace Marble; StationListParser::StationListParser( QObject *parent ) : QThread( parent ), QXmlStreamReader() { } StationListParser::~StationListParser() { wait( 1000 ); } void StationListParser::read() { m_list.clear(); while ( !atEnd() ) { readNext(); if ( isStartElement() ) { if (name() == QLatin1String("StationList")) readStationList(); else raiseError( QObject::tr("The file is not a valid file.") ); } } } QList StationListParser::stationList() const { return m_list; } void StationListParser::setPath( const QString& path ) { m_path = path; } void StationListParser::run() { QFile file( m_path ); if( !file.open( QIODevice::ReadOnly | QIODevice::Text ) ) { return; } setDevice( &file ); read(); } void StationListParser::readUnknownElement() { Q_ASSERT( isStartElement() ); while ( !atEnd() ) { readNext(); if ( isEndElement() ) break; if ( isStartElement() ) readUnknownElement(); } } void StationListParser::readStationList() { Q_ASSERT( isStartElement() && name() == QLatin1String("StationList")); while( !atEnd() ) { readNext(); if( isEndElement() ) break; if( isStartElement() ) { if (name() == QLatin1String("Station")) readStation(); else readUnknownElement(); } } } void StationListParser::readStation() { Q_ASSERT( isStartElement() && name() == QLatin1String("Station")); BBCStation station; while ( !atEnd() ) { readNext(); if( isEndElement() ) break; if( isStartElement() ) { if (name() == QLatin1String("name")) station.setName( readCharacters() ); else if (name() == QLatin1String("id")) station.setBbcId( readCharacters().toLong() ); else if (name() == QLatin1String("priority")) station.setPriority( readCharacters().toInt() ); else if (name() == QLatin1String("Point")) readPoint( &station ); else readUnknownElement(); } } // This find the right position in the sorted to insert the new item - QList::iterator i = qLowerBound( m_list.begin(), + QList::iterator i = std::lower_bound( m_list.begin(), m_list.end(), station ); // Insert the item on the right position in the list m_list.insert( i, station ); } QString StationListParser::readCharacters() { Q_ASSERT( isStartElement() ); QString string; while ( !atEnd() ) { readNext(); if ( isEndElement() ) break; if ( isStartElement() ) { readUnknownElement(); } if ( isCharacters() ) { string = text().toString(); } } return string; } void StationListParser::readPoint( BBCStation *station ) { Q_ASSERT( isStartElement() && name() == QLatin1String("Point")); while ( !atEnd() ) { readNext(); if ( isEndElement() ) break; if ( isStartElement() ) { if (name() == QLatin1String("coordinates")) { QString coorString = readCharacters(); QStringList coorList = coorString.split(QLatin1Char(',')); if ( coorList.size() >= 2 ) { GeoDataCoordinates coordinates( coorList.at( 0 ).toFloat() * DEG2RAD, coorList.at( 1 ).toFloat() * DEG2RAD ); station->setCoordinate( coordinates ); } } else readUnknownElement(); } } } #include "moc_StationListParser.cpp"