radioapp/radiouiengine/src/radiostation.cpp
branchGCC_SURGE
changeset 37 451b2e1545b2
parent 16 f54ebcfc1b80
parent 33 11b6825f0862
--- a/radioapp/radiouiengine/src/radiostation.cpp	Fri Jun 11 16:24:13 2010 +0100
+++ b/radioapp/radiouiengine/src/radiostation.cpp	Thu Jul 22 16:33:45 2010 +0100
@@ -25,13 +25,18 @@
 #include "radio_global.h"
 
 // Constants
-const QString KTagArtist = "artist";
-const QString KTagTitle = "title";
-const QString KLinkArtist = "<a href=\"" + KTagArtist + "\">";
-const QString KLinkTitle = "<a href=\"" + KTagTitle + "\">";
-const QString KLinkClose = "</a>";
+const QLatin1String HTML_TAG_START( "<font color='cyan'><u>" );
+const QLatin1String HTML_TAG_END( "</u></font>" );
 
-const char* callSign[KThreeLetterCallSignCount] =
+const uint LAST_CALLSIGN_CHAR_CODE = 25;
+const uint THREE_LETTER_CALLSIGN_COUNT = 72;
+const uint KXXX_CALLSIGN_PI_FIRST = 0x1000;
+const uint WXXX_CALLSIGN_PI_FIRST = 0x54A8;
+const uint WXXX_CALLSIGN_PI_LAST = 0x994F;
+const uint XXX_CALLSIGN_PI_FIRST = 0x9950;
+const uint XXX_CALLSIGN_PI_LAST = 0x99B9;
+
+const char* CALLSIGN_TABLE[THREE_LETTER_CALLSIGN_COUNT] =
    {"KBW", "KCY", "KDB", "KDF", "KEX", "KFH","KFI","KGA","KGB",
     "KGO", "KGU", "KGW", "KGY", "KHQ", "KID", "KIT", "KJR", "KLO",
     "KLZ", "KMA", "KMJ", "KNX", "KOA", "KOB", "KOY", "KPQ", "KQV",
@@ -41,7 +46,7 @@
     "WJW", "WJZ", "WKY", "WLS", "WLW", "WMC", "WMT", "WOC", "WOI",
     "WOL", "WOR", "WOW", "WRC", "WRR", "WSB", "WSM", "WWJ", "WWL"};
 
-const uint piCode[KThreeLetterCallSignCount] =
+const uint PI_CODE_TABLE[THREE_LETTER_CALLSIGN_COUNT] =
    {0x99A5, 0x99A6, 0x9990, 0x99A7, 0x9950, 0x9951, 0x9952, 0x9953,
     0x9991, 0x9954, 0x9955, 0x9956, 0x9957, 0x99AA, 0x9958, 0x9959,
     0x995A, 0x995B, 0x995C, 0x995D, 0x995E, 0x995F, 0x9960, 0x99AB,
@@ -52,14 +57,30 @@
     0x997C, 0x997D, 0x997E, 0x999E, 0x999F, 0x9981, 0x99A0, 0x9983,
     0x9984, 0x99A1, 0x99B9, 0x99A2, 0x99A3, 0x99A4, 0x9988, 0x9989};
 
-const uint KDisableLocalAreaCoverageMask = 0x0800;
+const uint DISABLE_LOCAL_AREA_COVERAGE_MASK = 0x0800;
+
+const int PS_NAME_CHANGE_THRESHOLD_SECONDS = 10;
+
+// Macros to help protect shared null and manual station instances to help debugging
+// Enabled in emulator or win32 builds by default
+#if defined __WINS__ || defined BUILD_WIN32
+#   define PROTECT_SHAREDNULL_AND_MANUALSTATION
+#endif
 
-const int KPsNameChangeThresholdSeconds = 10;
+#ifdef PROTECT_SHAREDNULL_AND_MANUALSTATION
+#   define ASSERT_SHARED_NULL_IS_INTACT \
+        Q_ASSERT_X( mData->mPresetIndex != SharedNull, "RadioStation", "Shared null modified illegally!" );
+#   define ASSERT_MANUAL_STATION \
+        Q_ASSERT_X( mData->mPresetIndex != ManualStation, "RadioStation", "Illegally modifying manual station" );
+#else
+#   define ASSERT_SHARED_NULL_IS_INTACT
+#   define ASSERT_MANUAL_STATION
+#endif // PROTECT_SHAREDNULL_AND_MANUALSTATION
 
 /**
  * Static shared data instance that is used by all default-constructed RadioStation instances
  */
-Q_GLOBAL_STATIC_WITH_ARGS( RadioStationPrivate, shared_null, ( RadioStation::SharedNull ) )
+static RadioStationPrivate shared_null( RadioStation::SharedNull, 0 );
 
 /*!
  *
@@ -67,7 +88,7 @@
 QString RadioStation::parseFrequency( uint frequency )
 {
     QString freqString;
-    freqString.sprintf( "%.1f", qreal( frequency ) / KFrequencyMultiplier );
+    freqString.sprintf( "%.1f", qreal( frequency ) / FREQUENCY_MULTIPLIER );
     return freqString;
 }
 
@@ -75,29 +96,24 @@
  *
  */
 RadioStation::RadioStation() :
-    QObject( 0 )
+    mData( &shared_null )
 {
-    mData = shared_null();
-    mData->ref.ref();
 }
 
 /*!
  *
  */
 RadioStation::RadioStation( const RadioStation& other ) :
-    QObject( 0 )
+    mData( other.mData )
 {
-    mData = other.mData;
-    mData->ref.ref();
 }
 
 /*!
  *
  */
 RadioStation::RadioStation( int presetIndex, uint frequency ) :
-    QObject( 0 )
+    mData( new RadioStationPrivate( presetIndex, frequency ) )
 {
-    mData = new RadioStationPrivate( presetIndex, frequency );
 }
 
 /*!
@@ -105,7 +121,6 @@
  */
 RadioStation::~RadioStation()
 {
-    decrementReferenceCount();
 }
 
 /*!
@@ -113,7 +128,7 @@
  */
 RadioStation& RadioStation::operator=( const RadioStation& other )
 {
-    qAtomicAssign( mData, other.mData );
+    mData = other.mData;
     return *this;
 }
 
@@ -122,9 +137,8 @@
  */
 void RadioStation::reset()
 {
-    decrementReferenceCount();
-    mData = shared_null();
-    mData->ref.ref();
+    mData = &shared_null;
+    ASSERT_SHARED_NULL_IS_INTACT
 }
 
 /*!
@@ -133,7 +147,6 @@
 void RadioStation::setChangeFlags( RadioStation::Change flags )
 {
     if ( mData->mChangeFlags != flags ) {
-        detach();
         mData->mChangeFlags = flags;
     }
 }
@@ -144,7 +157,7 @@
 void RadioStation::setPresetIndex( int presetIndex )
 {
     if ( mData->mPresetIndex != presetIndex ) {
-        detach();
+        ASSERT_MANUAL_STATION
         mData->mPresetIndex = presetIndex;
         mData->mChangeFlags |= RadioStation::PersistentDataChanged;
     }
@@ -156,9 +169,9 @@
 void RadioStation::setFrequency( uint frequency )
 {
     if ( mData->mFrequency != frequency ) {
-        detach();
         mData->mFrequency = frequency;
         mData->mChangeFlags |= RadioStation::PersistentDataChanged;
+        ASSERT_SHARED_NULL_IS_INTACT
     }
 }
 
@@ -170,7 +183,6 @@
     // Name emptiness is checked because this name setter is used by incoming RDS PS name
     // and empty names should be ignored
     if ( !name.isEmpty() && !mData->mRenamedByUser && mData->mName.compare( name ) != 0 ) {
-        detach();
         mData->mName = name.trimmed();
         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::NameChanged;
 
@@ -179,7 +191,7 @@
         QTime previousChange = mData->mLastPsNameChangeTime;
         mData->mLastPsNameChangeTime = QTime::currentTime();
         if ( previousChange.isValid() && mData->mPsType == RadioStation::Static &&
-             previousChange.secsTo( mData->mLastPsNameChangeTime ) < KPsNameChangeThresholdSeconds ) {
+             previousChange.secsTo( mData->mLastPsNameChangeTime ) < PS_NAME_CHANGE_THRESHOLD_SECONDS ) {
             LOG( "Station changed PS name too often. PS type changed to Dynamic" );
             mData->mPsType = RadioStation::Dynamic;
             mData->mDynamicPsText = mData->mName;
@@ -190,11 +202,13 @@
 
         //TODO: This is a temporary thing to see some URL. Remove this
         if ( !mData->mName.isEmpty() ) {
-            mData->mUrl = "<a href=\"buu\">www." + mData->mName.toLower() + ".fi</a>";
+            QString url = mData->mName.toLower().remove( " " );
+            mData->mUrl = "www." + url + ".fi";
         } else {
             mData->mUrl = "";
         }
         mData->mChangeFlags |= RadioStation::UrlChanged;
+        ASSERT_SHARED_NULL_IS_INTACT
     }
 }
 
@@ -204,9 +218,9 @@
 void RadioStation::setGenre( const int genre )
 {
     if ( mData->mGenre != genre ) {
-        detach();
         mData->mGenre = genre;
         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::GenreChanged;
+        ASSERT_SHARED_NULL_IS_INTACT
     }
 }
 
@@ -216,9 +230,9 @@
 void RadioStation::setUrl( const QString& url )
 {
     if ( mData->mUrl.compare( url ) != 0 ) {
-        detach();
         mData->mUrl = url;
         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::UrlChanged;
+        ASSERT_SHARED_NULL_IS_INTACT
     }
 }
 
@@ -230,40 +244,33 @@
 {
     LOG_FORMAT( "RadioStation::setPiCode new PI: %d", piCode );
     // toggling local area coverage bit code must not be interpreted as new PI code
-    if( region != RadioRegion::America )
-    {
-        piCode &= ~KDisableLocalAreaCoverageMask;
+    if ( region != RadioRegion::America ) {
+        piCode &= ~DISABLE_LOCAL_AREA_COVERAGE_MASK;
     }
 
     LOG_FORMAT( "stored PI: %d", mData->mPiCode );
     LOG_FORMAT( "call sign check done: %d", mData->mCallSignCheckDone );
     //prevent executing the below code when unnessesary
-    if ( mData->mPiCode != piCode || !mData->mCallSignCheckDone )
-	{
-        detach();
+    if ( mData->mPiCode != piCode || !mData->mCallSignCheckDone ) {
         mData->mPiCode = piCode;
         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::PiCodeChanged;
         // call sign not calculated for clear channel stations
 		//TODO: Remove magic numbers
-        if( ( (mData->mPiCode & 0xF000 ) >> 12 ) == 0x1 )
-        {
+        if ( ( (mData->mPiCode & 0xF000 ) >> 12 ) == 0x1 ) {
             LOG( "Clear channel station" );
             mData->mCallSignCheckDone = true;
-        }
-		// if America region, not PS name received and not user renamed station
-        else if ( region == RadioRegion::America && mData->mName.isEmpty() && !isRenamed() )
-        {
+        } else if ( region == RadioRegion::America && mData->mName.isEmpty() && !isRenamed() ) {
             LOG( "Calculate call sign" );
             mData->mName = piCodeToCallSign( mData->mPiCode );
             mData->mChangeFlags |= RadioStation::NameChanged;
         }
 
-        if ( mData->mChangeFlags.testFlag( RadioStation::PsTypeChanged ) )
-		{
+        if ( mData->mChangeFlags.testFlag( RadioStation::PsTypeChanged ) ) {
             LOG( "Call sign check done" );
             mData->mCallSignCheckDone = true;
         }
 
+        ASSERT_SHARED_NULL_IS_INTACT
         return true;
     }
     return false;
@@ -275,9 +282,9 @@
 void RadioStation::setPsType( PsType psType )
 {
     if ( mData->mPsType != psType ) {
-        detach();
         mData->mPsType = psType;
         mData->mChangeFlags |= RadioStation::PsTypeChanged;
+        ASSERT_SHARED_NULL_IS_INTACT
     }
 }
 
@@ -287,9 +294,9 @@
 void RadioStation::setRadioText( const QString& radioText )
 {
     if ( mData->mRadioText.compare( radioText ) != 0 ) {
-        detach();
         mData->mRadioText = radioText.isEmpty() ? "" : radioText.trimmed();
         mData->mChangeFlags |= RadioStation::RadioTextChanged;
+        ASSERT_SHARED_NULL_IS_INTACT
     }
 }
 
@@ -308,14 +315,7 @@
             return;
         }
 
-        detach();
-        QString replacement = "";
-        if ( rtPlusClass == RtPlus::Artist ) {
-            replacement = KLinkArtist;
-        } else if ( rtPlusClass == RtPlus::Title ) {
-            replacement = KLinkTitle;
-        }
-        replacement += rtPlusItem + KLinkClose;
+        const QString replacement = HTML_TAG_START + rtPlusItem + HTML_TAG_END;
 
         mData->mRadioText.replace( rtPlusItem, replacement );
         mData->mChangeFlags |= RadioStation::RadioTextChanged;
@@ -328,9 +328,9 @@
 void RadioStation::setDynamicPsText( const QString& dynamicPsText )
 {
     if ( mData->mDynamicPsText.compare( dynamicPsText ) != 0 ) {
-        detach();
         mData->mDynamicPsText = dynamicPsText;
         mData->mChangeFlags |= RadioStation::DynamicPsChanged;
+        ASSERT_SHARED_NULL_IS_INTACT
     }
 }
 
@@ -339,7 +339,7 @@
  */
 bool RadioStation::isValid() const
 {
-    return mData->mPresetIndex >= 0 && mData->mFrequency > 0;
+    return mData->mFrequency > 0 && ( isType( ManualStation ) || mData->mPresetIndex >= 0 );
 }
 
 /*!
@@ -358,7 +358,6 @@
     // We don't check for name emptiness because this setter is used also to remove the renaming
     // of a station by setting an empty name
     if ( mData->mName.compare( name ) != 0 ) {
-        detach();
         mData->mName = name;
         mData->mRenamedByUser = !name.isEmpty();
         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::NameChanged;
@@ -384,7 +383,7 @@
 /*!
  *
  */
-QString RadioStation::frequencyMhz() const
+QString RadioStation::frequencyString() const
 {
     return parseFrequency( mData->mFrequency );
 }
@@ -411,9 +410,9 @@
 void RadioStation::setFavorite( bool favorite )
 {
     if ( isFavorite() != favorite ) {
-        detach();
         favorite ? setType( RadioStation::Favorite ) : unsetType( RadioStation::Favorite );
         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::FavoriteChanged;
+        ASSERT_SHARED_NULL_IS_INTACT
     }
 }
 
@@ -436,21 +435,9 @@
 /*!
  *
  */
-bool RadioStation::hasPiCode() const
+int RadioStation::piCode() const
 {
-    return mData->mPiCode != -1;
-}
-
-/*!
- *
- */
-bool RadioStation::hasRds() const
-{
-    return hasPiCode() ||
-            mData->mGenre != -1 ||
-            !mData->mDynamicPsText.isEmpty() ||
-            !mData->mRadioText.isEmpty() ||
-            ( !mData->mName.isEmpty() && !isRenamed() );
+    return mData->mPiCode;
 }
 
 /*!
@@ -459,7 +446,6 @@
 void RadioStation::setType( RadioStation::Type type )
 {
     if ( !isType( type ) ) {
-        detach();
 
         // Check if favorite-status changed
         if ( mData->mType.testFlag( RadioStation::Favorite ) != type.testFlag( RadioStation::Favorite ) ) {
@@ -468,6 +454,7 @@
 
         mData->mType |= type;
         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::TypeChanged;
+        ASSERT_SHARED_NULL_IS_INTACT
     }
 }
 
@@ -477,7 +464,6 @@
 void RadioStation::unsetType( RadioStation::Type type )
 {
     if ( isType( type ) ) {
-        detach();
 
         // Check if favorite-status changed
         if ( mData->mType.testFlag( RadioStation::Favorite ) != type.testFlag( RadioStation::Favorite ) ) {
@@ -486,6 +472,7 @@
 
         mData->mType &= ~type;
         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::TypeChanged;
+        ASSERT_SHARED_NULL_IS_INTACT
     }
 }
 
@@ -551,22 +538,10 @@
 void RadioStation::resetChangeFlags()
 {
     if ( mData->mChangeFlags != RadioStation::NoChange ) {
-        detach();
         mData->mChangeFlags = RadioStation::NoChange;
     }
 }
 
-/**
- * Decrements the reference count of the implicitly shared data.
- */
-void RadioStation::decrementReferenceCount()
-{
-    if ( !mData->ref.deref() ) {
-        delete mData;
-        mData = 0;
-    }
-}
-
 /*!
  *
  */
@@ -576,21 +551,19 @@
 
     LOG_FORMAT( "RadioStation::piCodeToCallSign PI: %d", programmeIdentification );
     // call signs beginning with 'K'
-    if( ( programmeIdentification>=KKxxxCallSignPiFirst ) && ( programmeIdentification < KWxxxCallSignPiFirst ) ) {
+    if ( ( programmeIdentification>=KXXX_CALLSIGN_PI_FIRST ) && ( programmeIdentification < WXXX_CALLSIGN_PI_FIRST ) ) {
         callSign += "K";
-        callSign += iterateCallSign( KKxxxCallSignPiFirst, programmeIdentification );
+        callSign += iterateCallSign( KXXX_CALLSIGN_PI_FIRST, programmeIdentification );
     }
     // call signs beginning with 'W'
-    else if (( programmeIdentification >= KWxxxCallSignPiFirst ) && ( programmeIdentification <= KWxxxCallSignPiLast )) {
+    else if ( ( programmeIdentification >= WXXX_CALLSIGN_PI_FIRST ) && ( programmeIdentification <= WXXX_CALLSIGN_PI_LAST ) ) {
         callSign += "W";
-        callSign += iterateCallSign( KWxxxCallSignPiFirst, programmeIdentification );
+        callSign += iterateCallSign( WXXX_CALLSIGN_PI_FIRST, programmeIdentification );
     }
     // 3 letter only call signs
-    else if(( programmeIdentification >= KxxxCallSignPiFirst ) && ( programmeIdentification <= KxxxCallSignPiLast)) {
+    else if ( ( programmeIdentification >= XXX_CALLSIGN_PI_FIRST ) && ( programmeIdentification <= XXX_CALLSIGN_PI_LAST ) ) {
         callSign += callSignString( programmeIdentification );
-    }
-    else
-    {
+    } else {
         LOG( "RadioStation::piCodeToCallSign - Unhandled else" );
     }
 
@@ -607,10 +580,10 @@
     QString callSign;
     LOG_FORMAT( "RadioStation::iterateCallSign base: %d", piBase );
 
-    int sum(0), i(0);
-
+    int sum = 0;
+    int i = 0;
     while( sum < programmeIdentification ) {
-        i++;
+        ++i;
         sum = piBase + i * 676 + 0 + 0;
     }
     callSign += callSignChar( i - 1 );
@@ -619,7 +592,7 @@
     sum -= 676;
     i = 0;
     while( sum <= programmeIdentification ) {
-        i++;
+        ++i;
         sum = tmpSum + 0 + i * 26 + 0;
     }
     callSign += callSignChar( i - 1 );
@@ -628,7 +601,7 @@
     sum -= 26;
     i = 0;
     while( sum <= programmeIdentification ) {
-        i++;
+        ++i;
         sum = tmpSum + 0 + 0 + i;
     }
     callSign += callSignChar( i - 1 );
@@ -641,15 +614,15 @@
  */
 QString RadioStation::callSignString( uint programmeIdentification )
 {
-    for ( uint i = 0; i < KThreeLetterCallSignCount; ++i ) {
-        if( piCode[i] == programmeIdentification ) {
-            return callSign[i];
+    for ( uint i = 0; i < THREE_LETTER_CALLSIGN_COUNT; ++i ) {
+        if( PI_CODE_TABLE[i] == programmeIdentification ) {
+            return CALLSIGN_TABLE[i];
         }
     }
 
     LOG_FORMAT( "RadioStation::callSignString, Not found PI: %d", programmeIdentification );
 
-    return QString("????");
+    return QString( "????" );
 }
 
 /*!
@@ -658,37 +631,13 @@
 char RadioStation::callSignChar( uint decimalValue )
 {
     LOG_FORMAT( "RadioStation::callSignChar A+: %d", decimalValue );
-    if ( decimalValue <= KLastCallSignCharCode ) {
+    if ( decimalValue <= LAST_CALLSIGN_CHAR_CODE ) {
         return static_cast<char>( 'A' + decimalValue );
     }
     return '?';
 }
 
 /**
- * Detach from the implicitly shared data
- */
-void RadioStation::detach()
-{
-    if ( !isDetached() ) {
-        RadioStationPrivate* newData = new RadioStationPrivate( *mData );
-
-        decrementReferenceCount();
-
-        newData->ref = 1;
-        mData = newData;
-
-        // The shared null instance of the data has its preset index set to -200 (RadioStation::SharedNull).
-        // We change the preset index of the detached data to -100 (RadioStation::Invalid) just to ease
-        // debugging. This guarantees that the only instance that has value -200 is the actual shared null.
-        #ifdef _DEBUG
-        if ( mData->mPresetIndex == RadioStation::SharedNull ) {
-            mData->mPresetIndex = RadioStation::Invalid;
-        }
-        #endif
-    }
-}
-
-/**
  * Checks if the class is detached from implicitly shared data
  */
 bool RadioStation::isDetached() const