diff -r a2b50a479edf -r 6df133bd92e1 radioapp/radiouiengine/src/radiostationmodel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/radioapp/radiouiengine/src/radiostationmodel.cpp Fri Jun 04 10:21:36 2010 +0100 @@ -0,0 +1,726 @@ +/* +* 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 + +#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() +{ + delete d_ptr; +} + +/*! + * + */ +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 == RadioStationModel::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; + } + + 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 == RadioStationModel::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; + const int presetCount = d->mPresetStorage->presetCount(); + int index = d->mPresetStorage->firstPreset(); + LOG_FORMAT( "RadioStationModelPrivate::initialize: presetCount: %d, firstIndex: %d", presetCount, index ); + +#ifdef COMPILE_WITH_NEW_PRESET_UTILITY + while ( index >= 0 ) { +#else + index = 0; + while ( index < presetCount ) { +#endif // COMPILE_WITH_NEW_PRESET_UTILITY + + RadioStation station; + station.detach(); + + RadioStationIf* preset = static_cast( station.data_ptr() ); + if ( d->mPresetStorage->readPreset( index, *preset ) ) { + if ( station.isValid() ) { + d->mStations.insert( station.frequency(), station ); + } else { + LOG( "RadioStationModelPrivate::initialize: Invalid station!" ); + } + } + +#ifdef COMPILE_WITH_NEW_PRESET_UTILITY + index = d->mPresetStorage->nextPreset( index ); +#endif + } + + 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 < 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 ) +{ + Q_D( RadioStationModel ); + if ( d->mStations.contains( frequency ) ) { + station = d->mStations.value( frequency ); + return true; + } + return false; +} + +/*! + * 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 list = findFavorite ? d->favorites() : d->mStations.values(); + + // 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; +} + +/*! + * 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 = modelIndexFromFrequency( tempStation.frequency() ).row(); + 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 ); + } + } + } + } + + reset(); // TODO: Remove. this is a workaround to HbGridView update problem +} + +/*! + * Adds a new station to the list + */ +void RadioStationModel::addStation( const RadioStation& station ) +{ + Q_D( RadioStationModel ); + const int newIndex = findUnusedPresetIndex(); + LOG_FORMAT( "RadioStationModelPrivate::addStation: Adding station to index %d", newIndex ); + + RadioStation newStation = station; + newStation.setPresetIndex( newIndex ); + newStation.unsetType( RadioStation::Temporary ); + + // 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; + } + } + +// emit layoutAboutToBeChanged(); + beginInsertRows( QModelIndex(), row, row ); + + d->doSaveStation( newStation ); + + d->setCurrentStation( d->mWrapper->currentFrequency() ); + + endInsertRows(); + +// emit layoutChanged(); +} + +/*! + * 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::Temporary ) ) { + + 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( "RadioStationModelPrivate::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 ); + + // If PI code has been received, it is a local station + if ( newStation.hasPiCode() ) { + newStation.setType( RadioStation::LocalStation ); + } + + // 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( "RadioStationModelPrivate::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( "RadioStationModelPrivate::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 RadioStationModel::stationsInRange( uint minFrequency, uint maxFrequency ) +{ + Q_D( RadioStationModel ); + QList 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 + */ +QModelIndex RadioStationModel::modelIndexFromFrequency( uint frequency ) +{ + RadioStation station; + if ( findFrequency( frequency, station ) ) { + return index( findPresetIndex( station.presetIndex() ), 0 ); + } + return QModelIndex(); +} + +/*! + * 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 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( "RadioStationModelPrivate::findUnusedPresetIndex, index: %d", index ); + return index; +} + +/*! + * Used by the RDS data setters to find the correct station where the data is set + */ +RadioStation RadioStationModel::findCurrentStation( uint frequency ) +{ + Q_D( RadioStationModel ); + RadioStation station = *d->mCurrentStation; + if ( station.frequency() != frequency ) { + if ( !findFrequency( frequency, station ) ) { + return RadioStation(); + } + } + return station; +}