--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/radioapp/radiouiengine/src/radiostationmodel.cpp Tue Aug 31 15:15:02 2010 +0300
@@ -0,0 +1,753 @@
+/*
+* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+// System includes
+#include <QStringList>
+
+// User includes
+#include "radiostationmodel.h"
+#include "radiostationmodel_p.h"
+#include "radiopresetstorage.h"
+#include "radioenginewrapper.h"
+#include "radiouiengine.h"
+#include "radiouiengine_p.h"
+#include "radiostation.h"
+#include "radiostation_p.h"
+#include "radiologger.h"
+
+/*!
+ *
+ */
+static QString parseLine( const RadioStation& station )
+{
+ QString line = "";
+ const QString parsedFrequency = qtTrId( "txt_rad_dblist_l1_mhz" ).arg( RadioStation::parseFrequency( station.frequency() ) );
+ line.append( parsedFrequency );
+
+ QString name = station.name();
+ if ( !name.isEmpty() )
+ {
+ line.append( " - " );
+ line.append( name.trimmed() );
+ }
+
+ LOG_FORMAT( "RadioStationModel: Returning line %s", GETSTRING(line) );
+ return line;
+}
+
+/*!
+ *
+ */
+RadioStationModel::RadioStationModel( RadioUiEnginePrivate& uiEngine ) :
+ QAbstractListModel( &uiEngine.api() ),
+ d_ptr( new RadioStationModelPrivate( this, uiEngine ) )
+{
+}
+
+/*!
+ *
+ */
+RadioStationModel::~RadioStationModel()
+{
+}
+
+/*!
+ *
+ */
+Qt::ItemFlags RadioStationModel::flags ( const QModelIndex& index ) const
+{
+ Qt::ItemFlags flags = QAbstractListModel::flags( index );
+ flags |= Qt::ItemIsEditable;
+ return flags;
+}
+
+/*!
+ *
+ */
+int RadioStationModel::rowCount( const QModelIndex& parent ) const
+{
+ Q_UNUSED( parent );
+ Q_D( const RadioStationModel );
+ const int count = d->mStations.keys().count();
+ return count;
+}
+
+/*!
+ * Checks the given station and emits signals based on what member variables had been changed
+ */
+QVariant RadioStationModel::data( const QModelIndex& index, int role ) const
+{
+ if ( !index.isValid() ) {
+ return QVariant();
+ }
+
+ Q_D( const RadioStationModel );
+ if ( role == Qt::DisplayRole ) {
+ RadioStation station = stationAt( index.row() );
+ QString firstLine = parseLine( station );
+ if ( d->mDetailLevel.testFlag( RadioStationModel::ShowGenre ) ) {
+ QStringList list;
+ list.append( firstLine );
+ QString genre = " "; // Empty space so that the listbox generates the second row
+ if ( station.genre() != -1 ) {
+ genre = d->mUiEngine.api().genreToString( station.genre(), GenreTarget::StationsList );
+ }
+ list.append( genre );
+
+ return list;
+ }
+
+ return firstLine;
+ } else if ( role == RadioRole::RadioStationRole ) {
+ QVariant variant;
+ variant.setValue( stationAt( index.row() ) );
+ return variant;
+ } else if ( role == Qt::DecorationRole &&
+ d->mDetailLevel.testFlag( RadioStationModel::ShowIcons ) ) {
+ RadioStation station = stationAt( index.row() );
+ QVariantList list;
+ if ( station.isFavorite() && !d->mFavoriteIcon.isNull() ) {
+ list.append( d->mFavoriteIcon );
+ } else {
+ list.append( QIcon() );
+ }
+ if ( currentStation().frequency() == station.frequency() && !d->mNowPlayingIcon.isNull() ) {
+ list.append( d->mNowPlayingIcon );
+ }
+ return list;
+ } else if ( role == RadioRole::IsFavoriteRole ) {
+ QVariant variant;
+ variant.setValue( stationAt( index.row() ).isFavorite() );
+ return variant;
+ }
+
+ return QVariant();
+}
+
+/*!
+ * Checks the given station and emits signals based on what member variables had been changed
+ */
+bool RadioStationModel::setData( const QModelIndex& index, const QVariant& value, int role )
+{
+ Q_UNUSED( index );
+
+ if ( role == RadioRole::ToggleFavoriteRole ) {
+ const uint frequency = value.toUInt();
+ RadioStation station;
+ if ( findFrequency( frequency, station ) ) {
+ setFavoriteByPreset( station.presetIndex(), !station.isFavorite() );
+ } else {
+ setFavoriteByFrequency( frequency, true );
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ * Called by the engine to initialize the list with given amount of presets
+ */
+void RadioStationModel::initialize( RadioPresetStorage* storage, RadioEngineWrapper* wrapper )
+{
+ Q_D( RadioStationModel );
+ d->mPresetStorage = storage;
+ d->mWrapper = wrapper;
+
+ int index = d->mPresetStorage->firstPreset();
+ LOG_FORMAT( "RadioStationModel::initialize: presetCount: %d, firstIndex: %d",
+ d->mPresetStorage->presetCount(), index );
+
+ while ( index >= 0 ) {
+ RadioStation station;
+
+ RadioStationIf* stationInterface = static_cast<RadioStationIf*>( station.data_ptr() );
+ if ( d->mPresetStorage->readPreset( index, *stationInterface ) ) {
+ if ( station.isValid() && d->mWrapper->isFrequencyValid( station.frequency() ) ) {
+
+#ifdef INIT_STATIONS_WITH_DUMMY_RT
+ station.setGenre( GenreEurope::RdsChildrensProgrammes );
+ if ( index % 3 == 0 ) {
+ station.setName( "Radio Rock" );
+ station.setRadioText( "Now playing: <font color='cyan'>The Presidents of the United States of America</font> - <font color='cyan'>Dune Buggy and diipa daapa jhkjhui erjlkej rewjtl</font>" );
+ } else if ( index % 2 == 0 ) {
+ station.setName( "Radio Rock" );
+ } else {
+ station.setDynamicPsText( "DYN PS" );
+ }
+#endif // INIT_STATIONS_WITH_DUMMY_RT
+
+ // Check if the station seems to send RDS or not.
+ // Note that radiotext is not checked because it is not saved to cenrep
+ // TODO: Consider saving this state flag to cenrep
+ if ( ( station.hasName() && !station.isRenamed() ) || station.hasUrl() ) {
+ static_cast<RadioStationIf*>( station.data_ptr() )->setStationHasSentRds( true );
+ }
+
+ d->mStations.insert( station.frequency(), station );
+ } else {
+ LOG( "RadioStationModel::initialize: Invalid station!" );
+ LOG_FORMAT( "Invalid station freq: %d", station.frequency() );
+ }
+ }
+
+ index = d->mPresetStorage->nextPreset( index );
+ }
+
+ d->setCurrentStation( d->mWrapper->currentFrequency() );
+
+ wrapper->addObserver( d );
+}
+
+/*!
+ * Sets the icons to be used in the lists
+ */
+void RadioStationModel::setIcons( const QIcon& favoriteIcon, const QIcon& nowPlayingIcon )
+{
+ Q_D( RadioStationModel );
+ d->mFavoriteIcon = favoriteIcon;
+ d->mNowPlayingIcon = nowPlayingIcon;
+}
+
+/*!
+ * Returns a reference to the station handler interface
+ */
+RadioStationHandlerIf& RadioStationModel::stationHandlerIf()
+{
+ Q_D( RadioStationModel );
+ return *d;
+}
+
+/*!
+ * Returns a reference to the underlying QList so that it can be easily looped
+ */
+const Stations& RadioStationModel::list() const
+{
+ Q_D( const RadioStationModel );
+ return d->mStations;
+}
+
+/*!
+ * Returns the station at the given index.
+ */
+RadioStation RadioStationModel::stationAt( int index ) const
+{
+ // Get the value from the keys list instead of directly accessing the values list
+ // because QMap may have added a default-constructed value to the values list
+ Q_D( const RadioStationModel );
+ if ( index >= 0 && index < d->mStations.keys().count() ) {
+ uint frequency = d->mStations.keys().at( index );
+ return d->mStations.value( frequency );
+ }
+ return RadioStation();
+}
+
+/*!
+ * Finds a station by frequency
+ */
+bool RadioStationModel::findFrequency( uint frequency, RadioStation& station, FindCriteria::Criteria criteria ) const
+{
+ Q_D( const RadioStationModel );
+
+ if ( criteria == FindCriteria::IncludeManualStation && d->mCurrentStation->frequency() == frequency ) {
+ station = *d->mCurrentStation;
+ return true;
+ }
+
+ if ( d->mStations.contains( frequency ) ) {
+ station = d->mStations.value( frequency );
+ return true;
+ }
+ return false;
+}
+
+/*!
+ * Convenience function to find a radio station.
+ */
+RadioStation RadioStationModel::findStation( uint frequency, FindCriteria::Criteria criteria ) const
+{
+ RadioStation station;
+ findFrequency( frequency, station, criteria ); // Return value ignored
+ return station;
+}
+
+/*!
+ * Finds a station by preset index
+ */
+int RadioStationModel::findPresetIndex( int presetIndex )
+{
+ Q_D( RadioStationModel );
+ int index = 0;
+ foreach( const RadioStation& tempStation, d->mStations ) {
+ if ( tempStation.presetIndex() == presetIndex ) {
+ return index;
+ }
+ ++index;
+ }
+
+ return RadioStation::NotFound;
+}
+
+/*!
+ * Finds a station by preset index
+ */
+int RadioStationModel::findPresetIndex( int presetIndex, RadioStation& station )
+{
+ Q_D( RadioStationModel );
+ const int index = findPresetIndex( presetIndex );
+ if ( index != RadioStation::NotFound ) {
+ station = d->mStations.values().at( index );
+ }
+ return index;
+}
+
+/*!
+ * Finds the closest station from the given frequency
+ */
+RadioStation RadioStationModel::findClosest( const uint frequency, StationSkip::Mode mode )
+{
+ Q_D( RadioStationModel );
+ const bool findFavorite = mode == StationSkip::PreviousFavorite || mode == StationSkip::NextFavorite;
+ const bool findNext = mode == StationSkip::Next || mode == StationSkip::NextFavorite;
+ QList<RadioStation> list = findFavorite ? d->favorites() : d->mStations.values();
+
+ if ( list.isEmpty() ) {
+ return RadioStation();
+ }
+
+ // Find the previous and next station from current frequency
+ RadioStation previous;
+ RadioStation next;
+ foreach( const RadioStation& station, list ) {
+ const uint testFreq = station.frequency();
+ if ( testFreq == frequency ) {
+ continue;
+ }
+
+ if ( testFreq > frequency ) {
+ next = station;
+ break;
+ }
+ previous = station;
+ }
+
+ // Check if we need to loop around
+ if ( findNext && !next.isValid() ) {
+ next = list.first();
+ } else if ( !findNext && !previous.isValid() ) {
+ previous = list.last();
+ }
+
+ return findNext ? next : previous;
+}
+
+/*!
+ * Checks if the model contains the given frequency
+ */
+bool RadioStationModel::contains( const uint frequency ) const
+{
+ RadioStation unused;
+ return findFrequency( frequency, unused );
+}
+
+/*!
+ * Removes a station by frequency
+ */
+void RadioStationModel::removeByFrequency( uint frequency )
+{
+ RadioStation station;
+ if ( findFrequency( frequency, station ) ) {
+ removeStation( station );
+ }
+}
+
+/*!
+ * Removes a station by preset index
+ */
+void RadioStationModel::removeByPresetIndex( int presetIndex )
+{
+ RadioStation station;
+ const int index = findPresetIndex( presetIndex, station );
+ if ( index >= 0 ) {
+ removeStation( station );
+ }
+}
+
+/*!
+ * Removes the given station
+ */
+void RadioStationModel::removeStation( const RadioStation& station )
+{
+ Q_D( RadioStationModel );
+ const uint frequency = station.frequency();
+ if ( d->mStations.contains( frequency ) ) {
+
+ // If we are removing the current station, copy its data to the current station pointer
+ // to keep all of the received RDS data still available. They will be discarded when
+ // the user tunes to another frequency, but they are available if the user decides to add it back.
+ if ( d->mCurrentStation->frequency() == frequency ) {
+ *d->mCurrentStation = station;
+ }
+
+ // Copy the station to a temporary variable that can be used as signal parameter
+ RadioStation tempStation = station;
+
+ const int row = indexFromFrequency( tempStation.frequency() );
+ beginRemoveRows( QModelIndex(), row, row );
+
+ d->mPresetStorage->deletePreset( tempStation.presetIndex() );
+ d->mStations.remove( frequency );
+
+ d->mCurrentStation = NULL;
+ d->setCurrentStation( d->mWrapper->currentFrequency() );
+
+ endRemoveRows();
+ }
+}
+
+/*!
+ * Public slot
+ * Removes all stations
+ */
+void RadioStationModel::removeAll( RemoveMode mode )
+{
+ Q_D( RadioStationModel );
+ if ( d->mStations.count() == 0 ) {
+ return;
+ }
+
+ if ( mode == RemoveAll ) {
+ beginRemoveRows( QModelIndex(), 0, rowCount() - 1 );
+
+ // Preset utility deletes all presets with index -1
+ bool success = d->mPresetStorage->deletePreset( -1 );
+ Q_UNUSED( success );
+ RADIO_ASSERT( success, "FMRadio", "Failed to remove station" );
+
+ d->mStations.clear();
+ d->mCurrentStation = NULL;
+ d->setCurrentStation( d->mWrapper->currentFrequency() );
+
+ endRemoveRows();
+ } else {
+ foreach( const RadioStation& station, d->mStations ) {
+
+ if ( mode == RemoveLocalStations ) {
+ if ( station.isType( RadioStation::LocalStation ) && !station.isFavorite() ) {
+ removeStation( station );
+ }
+ } else {
+ if ( station.isFavorite() ) {
+ RadioStation newStation( station );
+ newStation.setFavorite( false );
+ saveStation( newStation );
+ }
+ }
+ }
+ }
+}
+
+/*!
+ * Adds a new station to the list
+ */
+void RadioStationModel::addStation( const RadioStation& station )
+{
+ Q_D( RadioStationModel );
+ const int newIndex = findUnusedPresetIndex();
+ LOG_FORMAT( "RadioStationModel::addStation: Adding station to index %d", newIndex );
+
+ RadioStation newStation = station;
+ newStation.setPresetIndex( newIndex );
+ newStation.unsetType( RadioStation::ManualStation );
+
+ // We have to call beginInsertRows() BEFORE the addition is actually done so we must figure out where
+ // the new station will go in the sorted frequency order
+ int row = 0;
+ const int count = rowCount();
+ if ( count > 1 ) {
+ Stations::const_iterator iter = d->mStations.upperBound( newStation.frequency() );
+ if ( d->mStations.contains( iter.key() ) ) {
+ row = d->mStations.keys().indexOf( iter.key() );
+ } else {
+ row = count;
+ }
+ } else if ( count == 1 ) {
+ uint existingFreq = d->mStations.keys().first();
+ if ( station.frequency() > existingFreq ) {
+ row = 1;
+ }
+ }
+
+ beginInsertRows( QModelIndex(), row, row );
+
+ d->doSaveStation( newStation );
+
+ d->setCurrentStation( d->mWrapper->currentFrequency() );
+
+ endInsertRows();
+
+ // Not all UI components listen to rowsInserted() signal so emit the favorite signal
+ if ( newStation.isFavorite() ) {
+ emit favoriteChanged( *d->mCurrentStation );
+ }
+}
+
+/*!
+ * Saves the given station. It is expected to already exist in the list
+ */
+void RadioStationModel::saveStation( RadioStation& station )
+{
+ Q_D( RadioStationModel );
+ const bool stationHasChanged = station.hasChanged();
+ RadioStation::Change changeFlags = station.changeFlags();
+ station.resetChangeFlags();
+
+ if ( station.isType( RadioStation::ManualStation ) ) {
+
+ d->mManualStation = station;
+ emitChangeSignals( station, changeFlags );
+
+ } else if ( station.isValid() && stationHasChanged && d->mStations.contains( station.frequency() )) {
+
+ d->doSaveStation( station, changeFlags.testFlag( RadioStation::PersistentDataChanged ) );
+ d->setCurrentStation( d->mWrapper->currentFrequency() );
+
+ emitChangeSignals( station, changeFlags );
+ }
+}
+
+/*!
+ * Finds number of favorite stations
+ */
+int RadioStationModel::favoriteCount()
+{
+ Q_D( const RadioStationModel );
+ return d->favorites().count();
+}
+
+/*!
+ * Changes the favorite status of a station by its frequency. If the station does
+ * not yet exist, it is added.
+ */
+void RadioStationModel::setFavoriteByFrequency( uint frequency, bool favorite )
+{
+ Q_D( RadioStationModel );
+ if ( d->mWrapper->isFrequencyValid( frequency ) ) {
+ LOG_FORMAT( "RadioStationModel::setFavoriteByFrequency, frequency: %d", frequency );
+ RadioStation station;
+ if ( findFrequency( frequency, station ) ) { // Update existing preset
+ if ( station.isFavorite() != favorite ) {
+ station.setFavorite( favorite );
+ saveStation( station );
+ }
+ } else if ( favorite ) { // Add new preset if setting as favorite
+ RadioStation newStation;
+ if ( d->mCurrentStation->frequency() == frequency ) {
+ newStation = *d->mCurrentStation;
+ } else {
+ LOG( "CurrentStation frequency mismatch!" );
+ newStation.setFrequency( frequency );
+ }
+
+ newStation.setType( RadioStation::LocalStation | RadioStation::Favorite );
+
+ // Emit the signals only after adding the preset and reinitializing the current station
+ // because the UI will probably query the current station in its slots that get called.
+ addStation( newStation );
+ }
+ }
+}
+
+/*!
+ * Changes the favorite status of a station by its preset index
+ */
+void RadioStationModel::setFavoriteByPreset( int presetIndex, bool favorite )
+{
+ LOG_FORMAT( "RadioStationModel::setFavoriteByPreset, presetIndex: %d", presetIndex );
+ RadioStation station;
+ if ( findPresetIndex( presetIndex, station ) != RadioStation::NotFound ) {
+ station.setFavorite( favorite );
+ saveStation( station );
+ }
+}
+
+/*!
+ * Renames a station by its preset index
+ */
+void RadioStationModel::renameStation( int presetIndex, const QString& name )
+{
+ LOG_FORMAT( "RadioStationModel::renameStation, presetIndex: %d, name: %s", presetIndex, GETSTRING(name) );
+ RadioStation station;
+ if ( findPresetIndex( presetIndex, station ) != RadioStation::NotFound ) {
+ station.setUserDefinedName( name );
+ saveStation( station );
+ }
+}
+
+/*!
+ *
+ */
+void RadioStationModel::setFavorites( const QModelIndexList& favorites )
+{
+ foreach ( const QModelIndex& index, favorites ) {
+ RadioStation station = stationAt( index.row() );
+ RADIO_ASSERT( station.isValid() , "RadioStationModel::setFavorites", "invalid RadioStation");
+ setFavoriteByPreset( station.presetIndex(), true );
+ }
+}
+
+/*!
+ * Returns the currently tuned station
+ */
+RadioStation& RadioStationModel::currentStation()
+{
+ Q_D( RadioStationModel );
+ return *d->mCurrentStation;
+}
+
+/*!
+ * Returns the currently tuned station
+ */
+const RadioStation& RadioStationModel::currentStation() const
+{
+ Q_D( const RadioStationModel );
+ return *d->mCurrentStation;
+}
+
+/*!
+ * Sets the model detail level
+ */
+void RadioStationModel::setDetail( Detail level )
+{
+ Q_D( RadioStationModel );
+ d->mDetailLevel = level;
+}
+
+/*!
+ * Returns a list of radio stations in the given frequency range
+ */
+QList<RadioStation> RadioStationModel::stationsInRange( uint minFrequency, uint maxFrequency )
+{
+ Q_D( RadioStationModel );
+ QList<RadioStation> stations;
+ foreach( const RadioStation& station, d->mStations ) {
+ if ( station.frequency() >= minFrequency && station.frequency() <= maxFrequency ) {
+ stations.append( station );
+ }
+ }
+
+ return stations;
+}
+
+/*!
+ * Returns the model index corresponding to the given frequency
+ */
+int RadioStationModel::indexFromFrequency( uint frequency )
+{
+ RadioStation station;
+ if ( findFrequency( frequency, station ) ) {
+ return findPresetIndex( station.presetIndex() );
+ }
+ return -1;
+}
+
+/*!
+ * Private slot
+ * Timer timeout slot to indicate that the dynamic PS check has ended
+ */
+void RadioStationModel::dynamicPsCheckEnded()
+{
+ Q_D( RadioStationModel );
+ LOG_TIMESTAMP( "Finished dynamic PS check." );
+ if ( d->mCurrentStation->psType() != RadioStation::Dynamic && !d->mCurrentStation->dynamicPsText().isEmpty() )
+ {
+ d->mCurrentStation->setPsType( RadioStation::Static );
+ d->mCurrentStation->setName( d->mCurrentStation->dynamicPsText() );
+ d->mCurrentStation->setDynamicPsText( "" );
+ saveStation( *d->mCurrentStation );
+ }
+}
+
+/*!
+ * Checks the given station and emits signals based on what member variables had been changed
+ */
+void RadioStationModel::emitChangeSignals( const RadioStation& station, RadioStation::Change flags )
+{
+ if ( flags.testFlag( RadioStation::NameChanged ) ||
+ flags.testFlag( RadioStation::GenreChanged ) ||
+ flags.testFlag( RadioStation::UrlChanged ) ||
+ flags.testFlag( RadioStation::TypeChanged ) ||
+ flags.testFlag( RadioStation::PiCodeChanged ) ) {
+
+ // Create a temporary RadioStation for the duration of the signal-slot processing
+ // The receivers can ask the station what data has changed and update accordingly
+ RadioStation tempStation( station );
+ tempStation.setChangeFlags( flags );
+ emit stationDataChanged( tempStation );
+
+ emitDataChanged( tempStation );
+ }
+
+ if ( flags.testFlag( RadioStation::RadioTextChanged ) ) {
+ emit radioTextReceived( station );
+ emitDataChanged( station );
+ }
+
+ if ( flags.testFlag( RadioStation::DynamicPsChanged ) ) {
+ emit dynamicPsChanged( station );
+ emitDataChanged( station );
+ }
+
+ if ( flags.testFlag( RadioStation::FavoriteChanged ) && station.isValid() ) {
+ emit favoriteChanged( station );
+ emitDataChanged( station );
+ }
+}
+
+/*!
+ *
+ */
+void RadioStationModel::emitDataChanged( const RadioStation& station )
+{
+ const int row = findPresetIndex( station.presetIndex() );
+ QModelIndex top = index( row, 0, QModelIndex() );
+ QModelIndex bottom = index( row, 0, QModelIndex() );
+ emit dataChanged( top, bottom );
+}
+
+/*!
+ * Finds an unused preset index
+ */
+int RadioStationModel::findUnusedPresetIndex()
+{
+ Q_D( RadioStationModel );
+ QList<int> indexes;
+ foreach( const RadioStation& station, d->mStations ) {
+ if ( station.isValid() ) {
+ indexes.append( station.presetIndex() );
+ }
+ }
+
+ int index = 0;
+ for ( ; indexes.contains( index ); ++index ) {
+ // Nothing to do here
+ }
+
+ LOG_FORMAT( "RadioStationModel::findUnusedPresetIndex, index: %d", index );
+ return index;
+}