radioapp/radiouiengine/src/radiostation.cpp
changeset 23 a2b50a479edf
parent 19 afea38384506
child 24 6df133bd92e1
equal deleted inserted replaced
19:afea38384506 23:a2b50a479edf
     1 /*
       
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:
       
    15 *
       
    16 */
       
    17 
       
    18 // System includes
       
    19 #include <QTime>
       
    20 
       
    21 // User includes
       
    22 #include "radiostation.h"
       
    23 #include "radiostation_p.h"
       
    24 #include "radiologger.h"
       
    25 #include "radio_global.h"
       
    26 
       
    27 // Constants
       
    28 const QString KTagArtist = "artist";
       
    29 const QString KTagTitle = "title";
       
    30 const QString KLinkArtist = "<a href=\"" + KTagArtist + "\">";
       
    31 const QString KLinkTitle = "<a href=\"" + KTagTitle + "\">";
       
    32 const QString KLinkClose = "</a>";
       
    33 
       
    34 const char* callSign[KThreeLetterCallSignCount] =
       
    35    {"KBW", "KCY", "KDB", "KDF", "KEX", "KFH","KFI","KGA","KGB",
       
    36     "KGO", "KGU", "KGW", "KGY", "KHQ", "KID", "KIT", "KJR", "KLO",
       
    37     "KLZ", "KMA", "KMJ", "KNX", "KOA", "KOB", "KOY", "KPQ", "KQV",
       
    38     "KSD", "KSL", "KUJ", "KUT", "KVI", "KWG", "KXL", "KXO", "KYW",
       
    39     "WBT", "WBZ", "WDZ", "WEW", "WGH", "WGL", "WGN", "WGR", "WGY",
       
    40     "WHA", "WHB", "WHK", "WHO", "WHP", "WIL", "WIP", "WIS", "WJR",
       
    41     "WJW", "WJZ", "WKY", "WLS", "WLW", "WMC", "WMT", "WOC", "WOI",
       
    42     "WOL", "WOR", "WOW", "WRC", "WRR", "WSB", "WSM", "WWJ", "WWL"};
       
    43 
       
    44 const uint piCode[KThreeLetterCallSignCount] =
       
    45    {0x99A5, 0x99A6, 0x9990, 0x99A7, 0x9950, 0x9951, 0x9952, 0x9953,
       
    46     0x9991, 0x9954, 0x9955, 0x9956, 0x9957, 0x99AA, 0x9958, 0x9959,
       
    47     0x995A, 0x995B, 0x995C, 0x995D, 0x995E, 0x995F, 0x9960, 0x99AB,
       
    48     0x9992, 0x9993, 0x9964, 0x9994, 0x9965, 0x9966, 0x9995, 0x9967,
       
    49     0x9968, 0x9996, 0x9997, 0x996B, 0x9999, 0x996D, 0x996E, 0x996F,
       
    50     0x999A, 0x9971, 0x9972, 0x9973, 0x999B, 0x9975, 0x9976, 0x9977,
       
    51     0x9978, 0x999C, 0x999D, 0x997A, 0x99B3, 0x997B, 0x99B4, 0x99B5,
       
    52     0x997C, 0x997D, 0x997E, 0x999E, 0x999F, 0x9981, 0x99A0, 0x9983,
       
    53     0x9984, 0x99A1, 0x99B9, 0x99A2, 0x99A3, 0x99A4, 0x9988, 0x9989};
       
    54 
       
    55 const uint KDisableLocalAreaCoverageMask = 0x0800;
       
    56 
       
    57 const int KPsNameChangeThresholdSeconds = 10;
       
    58 
       
    59 /**
       
    60  * Static shared data instance that is used by all default-constructed RadioStation instances
       
    61  */
       
    62 Q_GLOBAL_STATIC_WITH_ARGS( RadioStationPrivate, shared_null, ( RadioStation::SharedNull ) )
       
    63 
       
    64 /*!
       
    65  *
       
    66  */
       
    67 QString RadioStation::parseFrequency( uint frequency )
       
    68 {
       
    69     QString freqString;
       
    70     freqString.sprintf( "%.1f", qreal( frequency ) / KFrequencyMultiplier );
       
    71     return freqString;
       
    72 }
       
    73 
       
    74 /*!
       
    75  *
       
    76  */
       
    77 RadioStation::RadioStation() :
       
    78     QObject( 0 )
       
    79 {
       
    80     mData = shared_null();
       
    81     mData->ref.ref();
       
    82 }
       
    83 
       
    84 /*!
       
    85  *
       
    86  */
       
    87 RadioStation::RadioStation( const RadioStation& other ) :
       
    88     QObject( 0 )
       
    89 {
       
    90     mData = other.mData;
       
    91     mData->ref.ref();
       
    92 }
       
    93 
       
    94 /*!
       
    95  *
       
    96  */
       
    97 RadioStation::RadioStation( int presetIndex, uint frequency ) :
       
    98     QObject( 0 )
       
    99 {
       
   100     mData = new RadioStationPrivate( presetIndex, frequency );
       
   101 }
       
   102 
       
   103 /*!
       
   104  *
       
   105  */
       
   106 RadioStation::~RadioStation()
       
   107 {
       
   108     decrementReferenceCount();
       
   109 }
       
   110 
       
   111 /*!
       
   112  *
       
   113  */
       
   114 RadioStation& RadioStation::operator=( const RadioStation& other )
       
   115 {
       
   116     qAtomicAssign( mData, other.mData );
       
   117     return *this;
       
   118 }
       
   119 
       
   120 /*!
       
   121  *
       
   122  */
       
   123 void RadioStation::reset()
       
   124 {
       
   125     decrementReferenceCount();
       
   126     mData = shared_null();
       
   127     mData->ref.ref();
       
   128 }
       
   129 
       
   130 /*!
       
   131  *
       
   132  */
       
   133 void RadioStation::setChangeFlags( RadioStation::Change flags )
       
   134 {
       
   135     if ( mData->mChangeFlags != flags ) {
       
   136         detach();
       
   137         mData->mChangeFlags = flags;
       
   138     }
       
   139 }
       
   140 
       
   141 /*!
       
   142  *
       
   143  */
       
   144 void RadioStation::setPresetIndex( int presetIndex )
       
   145 {
       
   146     if ( mData->mPresetIndex != presetIndex ) {
       
   147         detach();
       
   148         mData->mPresetIndex = presetIndex;
       
   149         mData->mChangeFlags |= RadioStation::PersistentDataChanged;
       
   150     }
       
   151 }
       
   152 
       
   153 /*!
       
   154  *
       
   155  */
       
   156 void RadioStation::setFrequency( uint frequency )
       
   157 {
       
   158     if ( mData->mFrequency != frequency ) {
       
   159         detach();
       
   160         mData->mFrequency = frequency;
       
   161         mData->mChangeFlags |= RadioStation::PersistentDataChanged;
       
   162     }
       
   163 }
       
   164 
       
   165 /*!
       
   166  * Sets the preset name
       
   167  */
       
   168 void RadioStation::setName( const QString& name )
       
   169 {
       
   170     // Name emptiness is checked because this name setter is used by incoming RDS PS name
       
   171     // and empty names should be ignored
       
   172     if ( !name.isEmpty() && !mData->mRenamedByUser && mData->mName.compare( name ) != 0 ) {
       
   173         detach();
       
   174         mData->mName = name.trimmed();
       
   175         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::NameChanged;
       
   176 
       
   177         // Save the time when PS name changed and if the last change was too close to the current time
       
   178         // change the PS type to dynamic if it has already been incorrectly determined to be static.
       
   179         QTime previousChange = mData->mLastPsNameChangeTime;
       
   180         mData->mLastPsNameChangeTime = QTime::currentTime();
       
   181         if ( previousChange.isValid() && mData->mPsType == RadioStation::Static &&
       
   182              previousChange.secsTo( mData->mLastPsNameChangeTime ) < KPsNameChangeThresholdSeconds ) {
       
   183             LOG( "Station changed PS name too often. PS type changed to Dynamic" );
       
   184             mData->mPsType = RadioStation::Dynamic;
       
   185             mData->mDynamicPsText = mData->mName;
       
   186             mData->mName = "";
       
   187             mData->mChangeFlags |= RadioStation::PsTypeChanged | RadioStation::DynamicPsChanged;
       
   188             mData->mCallSignCheckDone = false;
       
   189         }
       
   190 
       
   191         //TODO: This is a temporary thing to see some URL. Remove this
       
   192         if ( !mData->mName.isEmpty() ) {
       
   193             mData->mUrl = "<a href=\"buu\">www." + mData->mName.toLower() + ".fi</a>";
       
   194         } else {
       
   195             mData->mUrl = "";
       
   196         }
       
   197         mData->mChangeFlags |= RadioStation::UrlChanged;
       
   198     }
       
   199 }
       
   200 
       
   201 /*!
       
   202  *
       
   203  */
       
   204 void RadioStation::setGenre( const int genre )
       
   205 {
       
   206     if ( mData->mGenre != genre ) {
       
   207         detach();
       
   208         mData->mGenre = genre;
       
   209         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::GenreChanged;
       
   210     }
       
   211 }
       
   212 
       
   213 /*!
       
   214  *
       
   215  */
       
   216 void RadioStation::setUrl( const QString& url )
       
   217 {
       
   218     if ( mData->mUrl.compare( url ) != 0 ) {
       
   219         detach();
       
   220         mData->mUrl = url;
       
   221         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::UrlChanged;
       
   222     }
       
   223 }
       
   224 
       
   225 /*!
       
   226  * Sets the PI code
       
   227  * @return true if code was changed, false if not
       
   228  */
       
   229 bool RadioStation::setPiCode( int piCode, RadioRegion::Region region )
       
   230 {
       
   231     LOG_FORMAT( "RadioStation::setPiCode new PI: %d", piCode );
       
   232     // toggling local area coverage bit code must not be interpreted as new PI code
       
   233     if( region != RadioRegion::America )
       
   234     {
       
   235         piCode &= ~KDisableLocalAreaCoverageMask;
       
   236     }
       
   237 
       
   238     LOG_FORMAT( "stored PI: %d", mData->mPiCode );
       
   239     LOG_FORMAT( "call sign check done: %d", mData->mCallSignCheckDone );
       
   240     //prevent executing the below code when unnessesary
       
   241     if ( mData->mPiCode != piCode || !mData->mCallSignCheckDone )
       
   242 	{
       
   243         detach();
       
   244         mData->mPiCode = piCode;
       
   245         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::PiCodeChanged;
       
   246         // call sign not calculated for clear channel stations
       
   247 		//TODO: Remove magic numbers
       
   248         if( ( (mData->mPiCode & 0xF000 ) >> 12 ) == 0x1 )
       
   249         {
       
   250             LOG( "Clear channel station" );
       
   251             mData->mCallSignCheckDone = true;
       
   252         }
       
   253 		// if America region, not PS name received and not user renamed station
       
   254         else if ( region == RadioRegion::America && mData->mName.isEmpty() && !isRenamed() )
       
   255         {
       
   256             LOG( "Calculate call sign" );
       
   257             mData->mName = piCodeToCallSign( mData->mPiCode );
       
   258             mData->mChangeFlags |= RadioStation::NameChanged;
       
   259         }
       
   260 
       
   261         if ( mData->mChangeFlags.testFlag( RadioStation::PsTypeChanged ) )
       
   262 		{
       
   263             LOG( "Call sign check done" );
       
   264             mData->mCallSignCheckDone = true;
       
   265         }
       
   266 
       
   267         return true;
       
   268     }
       
   269     return false;
       
   270 }
       
   271 
       
   272 /*!
       
   273  *
       
   274  */
       
   275 void RadioStation::setPsType( PsType psType )
       
   276 {
       
   277     if ( mData->mPsType != psType ) {
       
   278         detach();
       
   279         mData->mPsType = psType;
       
   280         mData->mChangeFlags |= RadioStation::PsTypeChanged;
       
   281     }
       
   282 }
       
   283 
       
   284 /*!
       
   285  *
       
   286  */
       
   287 void RadioStation::setRadioText( const QString& radioText )
       
   288 {
       
   289     if ( mData->mRadioText.compare( radioText ) != 0 ) {
       
   290         detach();
       
   291         mData->mRadioText = radioText.isEmpty() ? "" : radioText.trimmed();
       
   292         mData->mChangeFlags |= RadioStation::RadioTextChanged;
       
   293     }
       
   294 }
       
   295 
       
   296 /*!
       
   297  *
       
   298  */
       
   299 void RadioStation::setRadioTextPlus( const int rtPlusClass, const QString& rtPlusItem )
       
   300 {
       
   301     if ( !mData->mRadioText.isEmpty() &&
       
   302          !rtPlusItem.isEmpty() &&
       
   303          ( rtPlusClass == RtPlus::Artist || rtPlusClass == RtPlus::Title || rtPlusClass == RtPlus::Homepage) )
       
   304     {
       
   305         // Url is saved to its own variable and it is not highlighted from the radiotext
       
   306         if ( rtPlusClass == RtPlus::Homepage ) {
       
   307             setUrl( rtPlusItem );
       
   308             return;
       
   309         }
       
   310 
       
   311         detach();
       
   312         QString replacement = "";
       
   313         if ( rtPlusClass == RtPlus::Artist ) {
       
   314             replacement = KLinkArtist;
       
   315         } else if ( rtPlusClass == RtPlus::Title ) {
       
   316             replacement = KLinkTitle;
       
   317         }
       
   318         replacement += rtPlusItem + KLinkClose;
       
   319 
       
   320         mData->mRadioText.replace( rtPlusItem, replacement );
       
   321         mData->mChangeFlags |= RadioStation::RadioTextChanged;
       
   322     }
       
   323 }
       
   324 
       
   325 /*!
       
   326  *
       
   327  */
       
   328 void RadioStation::setDynamicPsText( const QString& dynamicPsText )
       
   329 {
       
   330     if ( mData->mDynamicPsText.compare( dynamicPsText ) != 0 ) {
       
   331         detach();
       
   332         mData->mDynamicPsText = dynamicPsText;
       
   333         mData->mChangeFlags |= RadioStation::DynamicPsChanged;
       
   334     }
       
   335 }
       
   336 
       
   337 /*!
       
   338  *
       
   339  */
       
   340 bool RadioStation::isValid() const
       
   341 {
       
   342     return mData->mPresetIndex >= 0 && mData->mFrequency > 0;
       
   343 }
       
   344 
       
   345 /*!
       
   346  *
       
   347  */
       
   348 QString RadioStation::name() const
       
   349 {
       
   350     return mData->mName.isEmpty() ? "" : mData->mName;
       
   351 }
       
   352 
       
   353 /*!
       
   354  *
       
   355  */
       
   356 void RadioStation::setUserDefinedName( const QString& name )
       
   357 {
       
   358     // We don't check for name emptiness because this setter is used also to remove the renaming
       
   359     // of a station by setting an empty name
       
   360     if ( mData->mName.compare( name ) != 0 ) {
       
   361         detach();
       
   362         mData->mName = name;
       
   363         mData->mRenamedByUser = !name.isEmpty();
       
   364         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::NameChanged;
       
   365     }
       
   366 }
       
   367 
       
   368 /*!
       
   369  *
       
   370  */
       
   371 bool RadioStation::isRenamed() const
       
   372 {
       
   373     return mData->mRenamedByUser;
       
   374 }
       
   375 
       
   376 /*!
       
   377  *
       
   378  */
       
   379 int RadioStation::genre() const
       
   380 {
       
   381     return mData->mGenre;
       
   382 }
       
   383 
       
   384 /*!
       
   385  *
       
   386  */
       
   387 QString RadioStation::frequencyMhz() const
       
   388 {
       
   389     return parseFrequency( mData->mFrequency );
       
   390 }
       
   391 
       
   392 /*!
       
   393  *
       
   394  */
       
   395 uint RadioStation::frequency() const
       
   396 {
       
   397     return mData->mFrequency;
       
   398 }
       
   399 
       
   400 /*!
       
   401  *
       
   402  */
       
   403 int RadioStation::presetIndex() const
       
   404 {
       
   405     return mData->mPresetIndex;
       
   406 }
       
   407 
       
   408 /*!
       
   409  *
       
   410  */
       
   411 void RadioStation::setFavorite( bool favorite )
       
   412 {
       
   413     if ( isFavorite() != favorite ) {
       
   414         detach();
       
   415         favorite ? setType( RadioStation::Favorite ) : unsetType( RadioStation::Favorite );
       
   416         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::FavoriteChanged;
       
   417     }
       
   418 }
       
   419 
       
   420 /*!
       
   421  *
       
   422  */
       
   423 bool RadioStation::isFavorite() const
       
   424 {
       
   425     return mData->mType.testFlag( RadioStation::Favorite );
       
   426 }
       
   427 
       
   428 /*!
       
   429  *
       
   430  */
       
   431 QString RadioStation::url() const
       
   432 {
       
   433     return mData->mUrl;
       
   434 }
       
   435 
       
   436 /*!
       
   437  *
       
   438  */
       
   439 bool RadioStation::hasPiCode() const
       
   440 {
       
   441     return mData->mPiCode != -1;
       
   442 }
       
   443 
       
   444 /*!
       
   445  *
       
   446  */
       
   447 bool RadioStation::hasRds() const
       
   448 {
       
   449     return hasPiCode() ||
       
   450             mData->mGenre != -1 ||
       
   451             !mData->mDynamicPsText.isEmpty() ||
       
   452             !mData->mRadioText.isEmpty() ||
       
   453             ( !mData->mName.isEmpty() && !isRenamed() );
       
   454 }
       
   455 
       
   456 /*!
       
   457  *
       
   458  */
       
   459 void RadioStation::setType( RadioStation::Type type )
       
   460 {
       
   461     if ( !isType( type ) ) {
       
   462         detach();
       
   463 
       
   464         // Check if favorite-status changed
       
   465         if ( mData->mType.testFlag( RadioStation::Favorite ) != type.testFlag( RadioStation::Favorite ) ) {
       
   466             mData->mChangeFlags |= RadioStation::FavoriteChanged;
       
   467         }
       
   468 
       
   469         mData->mType |= type;
       
   470         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::TypeChanged;
       
   471     }
       
   472 }
       
   473 
       
   474 /*!
       
   475  *
       
   476  */
       
   477 void RadioStation::unsetType( RadioStation::Type type )
       
   478 {
       
   479     if ( isType( type ) ) {
       
   480         detach();
       
   481 
       
   482         // Check if favorite-status changed
       
   483         if ( mData->mType.testFlag( RadioStation::Favorite ) != type.testFlag( RadioStation::Favorite ) ) {
       
   484             mData->mChangeFlags |= RadioStation::FavoriteChanged;
       
   485         }
       
   486 
       
   487         mData->mType &= ~type;
       
   488         mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::TypeChanged;
       
   489     }
       
   490 }
       
   491 
       
   492 /*!
       
   493  *
       
   494  */
       
   495 bool RadioStation::isType( RadioStation::Type type ) const
       
   496 {
       
   497     return ( mData->mType & type ) == type;
       
   498 }
       
   499 
       
   500 /*!
       
   501  *
       
   502  */
       
   503 RadioStation::PsType RadioStation::psType() const
       
   504 {
       
   505     return mData->mPsType;
       
   506 }
       
   507 
       
   508 /*!
       
   509  *
       
   510  */
       
   511 QString RadioStation::radioText() const
       
   512 {
       
   513     return mData->mRadioText;
       
   514 }
       
   515 
       
   516 /*!
       
   517  *
       
   518  */
       
   519 QString RadioStation::dynamicPsText() const
       
   520 {
       
   521     return mData->mDynamicPsText;
       
   522 }
       
   523 
       
   524 /*!
       
   525  *
       
   526  */
       
   527 RadioStation::Change RadioStation::changeFlags() const
       
   528 {
       
   529     return mData->mChangeFlags;
       
   530 }
       
   531 
       
   532 /*!
       
   533  *
       
   534  */
       
   535 bool RadioStation::hasDataChanged( RadioStation::Change flags ) const
       
   536 {
       
   537     return ( mData->mChangeFlags & flags ) == flags;
       
   538 }
       
   539 
       
   540 /*!
       
   541  *
       
   542  */
       
   543 bool RadioStation::hasChanged() const
       
   544 {
       
   545     return mData->mChangeFlags != RadioStation::NoChange;
       
   546 }
       
   547 
       
   548 /*!
       
   549  *
       
   550  */
       
   551 void RadioStation::resetChangeFlags()
       
   552 {
       
   553     if ( mData->mChangeFlags != RadioStation::NoChange ) {
       
   554         detach();
       
   555         mData->mChangeFlags = RadioStation::NoChange;
       
   556     }
       
   557 }
       
   558 
       
   559 /**
       
   560  * Decrements the reference count of the implicitly shared data.
       
   561  */
       
   562 void RadioStation::decrementReferenceCount()
       
   563 {
       
   564     if ( !mData->ref.deref() ) {
       
   565         delete mData;
       
   566         mData = 0;
       
   567     }
       
   568 }
       
   569 
       
   570 /*!
       
   571  *
       
   572  */
       
   573  QString RadioStation::piCodeToCallSign( uint programmeIdentification )
       
   574  {
       
   575     QString callSign;
       
   576 
       
   577     LOG_FORMAT( "RadioStation::piCodeToCallSign PI: %d", programmeIdentification );
       
   578     // call signs beginning with 'K'
       
   579     if( ( programmeIdentification>=KKxxxCallSignPiFirst ) && ( programmeIdentification < KWxxxCallSignPiFirst ) ) {
       
   580         callSign += "K";
       
   581         callSign += iterateCallSign( KKxxxCallSignPiFirst, programmeIdentification );
       
   582     }
       
   583     // call signs beginning with 'W'
       
   584     else if (( programmeIdentification >= KWxxxCallSignPiFirst ) && ( programmeIdentification <= KWxxxCallSignPiLast )) {
       
   585         callSign += "W";
       
   586         callSign += iterateCallSign( KWxxxCallSignPiFirst, programmeIdentification );
       
   587     }
       
   588     // 3 letter only call signs
       
   589     else if(( programmeIdentification >= KxxxCallSignPiFirst ) && ( programmeIdentification <= KxxxCallSignPiLast)) {
       
   590         callSign += callSignString( programmeIdentification );
       
   591     }
       
   592     else
       
   593     {
       
   594         LOG( "RadioStation::piCodeToCallSign - Unhandled else" );
       
   595     }
       
   596 
       
   597     LOG_FORMAT( "RadioStation::piCodeToCallSign, call sign: %s", GETSTRING(callSign) );
       
   598 
       
   599     return callSign;
       
   600 }
       
   601 
       
   602 /*!
       
   603  *
       
   604  */
       
   605 QString RadioStation::iterateCallSign( int piBase, int programmeIdentification )
       
   606 {
       
   607     QString callSign;
       
   608     LOG_FORMAT( "RadioStation::iterateCallSign base: %d", piBase );
       
   609 
       
   610     int sum(0), i(0);
       
   611 
       
   612     while( sum < programmeIdentification ) {
       
   613         i++;
       
   614         sum = piBase + i * 676 + 0 + 0;
       
   615     }
       
   616     callSign += callSignChar( i - 1 );
       
   617 
       
   618     int tmpSum( sum - 676 );
       
   619     sum -= 676;
       
   620     i = 0;
       
   621     while( sum <= programmeIdentification ) {
       
   622         i++;
       
   623         sum = tmpSum + 0 + i * 26 + 0;
       
   624     }
       
   625     callSign += callSignChar( i - 1 );
       
   626 
       
   627     tmpSum = sum - 26;
       
   628     sum -= 26;
       
   629     i = 0;
       
   630     while( sum <= programmeIdentification ) {
       
   631         i++;
       
   632         sum = tmpSum + 0 + 0 + i;
       
   633     }
       
   634     callSign += callSignChar( i - 1 );
       
   635 
       
   636     return callSign;
       
   637     }
       
   638 
       
   639 /*!
       
   640  *
       
   641  */
       
   642 QString RadioStation::callSignString( uint programmeIdentification )
       
   643 {
       
   644     for ( uint i = 0; i < KThreeLetterCallSignCount; ++i ) {
       
   645         if( piCode[i] == programmeIdentification ) {
       
   646             return callSign[i];
       
   647         }
       
   648     }
       
   649 
       
   650     LOG_FORMAT( "RadioStation::callSignString, Not found PI: %d", programmeIdentification );
       
   651 
       
   652     return QString("????");
       
   653 }
       
   654 
       
   655 /*!
       
   656  *
       
   657  */
       
   658 char RadioStation::callSignChar( uint decimalValue )
       
   659 {
       
   660     LOG_FORMAT( "RadioStation::callSignChar A+: %d", decimalValue );
       
   661     if ( decimalValue <= KLastCallSignCharCode ) {
       
   662         return static_cast<char>( 'A' + decimalValue );
       
   663     }
       
   664     return '?';
       
   665 }
       
   666 
       
   667 /**
       
   668  * Detach from the implicitly shared data
       
   669  */
       
   670 void RadioStation::detach()
       
   671 {
       
   672     if ( !isDetached() ) {
       
   673         RadioStationPrivate* newData = new RadioStationPrivate( *mData );
       
   674 
       
   675         decrementReferenceCount();
       
   676 
       
   677         newData->ref = 1;
       
   678         mData = newData;
       
   679 
       
   680         // The shared null instance of the data has its preset index set to -200 (RadioStation::SharedNull).
       
   681         // We change the preset index of the detached data to -100 (RadioStation::Invalid) just to ease
       
   682         // debugging. This guarantees that the only instance that has value -200 is the actual shared null.
       
   683         #ifdef _DEBUG
       
   684         if ( mData->mPresetIndex == RadioStation::SharedNull ) {
       
   685             mData->mPresetIndex = RadioStation::Invalid;
       
   686         }
       
   687         #endif
       
   688     }
       
   689 }
       
   690 
       
   691 /**
       
   692  * Checks if the class is detached from implicitly shared data
       
   693  */
       
   694 bool RadioStation::isDetached() const
       
   695 {
       
   696     return mData->ref == 1;
       
   697 }