branch | GCC_SURGE |
changeset 37 | 451b2e1545b2 |
parent 16 | f54ebcfc1b80 |
parent 33 | 11b6825f0862 |
26:6bcf277166c1 | 37:451b2e1545b2 |
---|---|
23 #include "radiostation_p.h" |
23 #include "radiostation_p.h" |
24 #include "radiologger.h" |
24 #include "radiologger.h" |
25 #include "radio_global.h" |
25 #include "radio_global.h" |
26 |
26 |
27 // Constants |
27 // Constants |
28 const QString KTagArtist = "artist"; |
28 const QLatin1String HTML_TAG_START( "<font color='cyan'><u>" ); |
29 const QString KTagTitle = "title"; |
29 const QLatin1String HTML_TAG_END( "</u></font>" ); |
30 const QString KLinkArtist = "<a href=\"" + KTagArtist + "\">"; |
30 |
31 const QString KLinkTitle = "<a href=\"" + KTagTitle + "\">"; |
31 const uint LAST_CALLSIGN_CHAR_CODE = 25; |
32 const QString KLinkClose = "</a>"; |
32 const uint THREE_LETTER_CALLSIGN_COUNT = 72; |
33 |
33 const uint KXXX_CALLSIGN_PI_FIRST = 0x1000; |
34 const char* callSign[KThreeLetterCallSignCount] = |
34 const uint WXXX_CALLSIGN_PI_FIRST = 0x54A8; |
35 const uint WXXX_CALLSIGN_PI_LAST = 0x994F; |
|
36 const uint XXX_CALLSIGN_PI_FIRST = 0x9950; |
|
37 const uint XXX_CALLSIGN_PI_LAST = 0x99B9; |
|
38 |
|
39 const char* CALLSIGN_TABLE[THREE_LETTER_CALLSIGN_COUNT] = |
|
35 {"KBW", "KCY", "KDB", "KDF", "KEX", "KFH","KFI","KGA","KGB", |
40 {"KBW", "KCY", "KDB", "KDF", "KEX", "KFH","KFI","KGA","KGB", |
36 "KGO", "KGU", "KGW", "KGY", "KHQ", "KID", "KIT", "KJR", "KLO", |
41 "KGO", "KGU", "KGW", "KGY", "KHQ", "KID", "KIT", "KJR", "KLO", |
37 "KLZ", "KMA", "KMJ", "KNX", "KOA", "KOB", "KOY", "KPQ", "KQV", |
42 "KLZ", "KMA", "KMJ", "KNX", "KOA", "KOB", "KOY", "KPQ", "KQV", |
38 "KSD", "KSL", "KUJ", "KUT", "KVI", "KWG", "KXL", "KXO", "KYW", |
43 "KSD", "KSL", "KUJ", "KUT", "KVI", "KWG", "KXL", "KXO", "KYW", |
39 "WBT", "WBZ", "WDZ", "WEW", "WGH", "WGL", "WGN", "WGR", "WGY", |
44 "WBT", "WBZ", "WDZ", "WEW", "WGH", "WGL", "WGN", "WGR", "WGY", |
40 "WHA", "WHB", "WHK", "WHO", "WHP", "WIL", "WIP", "WIS", "WJR", |
45 "WHA", "WHB", "WHK", "WHO", "WHP", "WIL", "WIP", "WIS", "WJR", |
41 "WJW", "WJZ", "WKY", "WLS", "WLW", "WMC", "WMT", "WOC", "WOI", |
46 "WJW", "WJZ", "WKY", "WLS", "WLW", "WMC", "WMT", "WOC", "WOI", |
42 "WOL", "WOR", "WOW", "WRC", "WRR", "WSB", "WSM", "WWJ", "WWL"}; |
47 "WOL", "WOR", "WOW", "WRC", "WRR", "WSB", "WSM", "WWJ", "WWL"}; |
43 |
48 |
44 const uint piCode[KThreeLetterCallSignCount] = |
49 const uint PI_CODE_TABLE[THREE_LETTER_CALLSIGN_COUNT] = |
45 {0x99A5, 0x99A6, 0x9990, 0x99A7, 0x9950, 0x9951, 0x9952, 0x9953, |
50 {0x99A5, 0x99A6, 0x9990, 0x99A7, 0x9950, 0x9951, 0x9952, 0x9953, |
46 0x9991, 0x9954, 0x9955, 0x9956, 0x9957, 0x99AA, 0x9958, 0x9959, |
51 0x9991, 0x9954, 0x9955, 0x9956, 0x9957, 0x99AA, 0x9958, 0x9959, |
47 0x995A, 0x995B, 0x995C, 0x995D, 0x995E, 0x995F, 0x9960, 0x99AB, |
52 0x995A, 0x995B, 0x995C, 0x995D, 0x995E, 0x995F, 0x9960, 0x99AB, |
48 0x9992, 0x9993, 0x9964, 0x9994, 0x9965, 0x9966, 0x9995, 0x9967, |
53 0x9992, 0x9993, 0x9964, 0x9994, 0x9965, 0x9966, 0x9995, 0x9967, |
49 0x9968, 0x9996, 0x9997, 0x996B, 0x9999, 0x996D, 0x996E, 0x996F, |
54 0x9968, 0x9996, 0x9997, 0x996B, 0x9999, 0x996D, 0x996E, 0x996F, |
50 0x999A, 0x9971, 0x9972, 0x9973, 0x999B, 0x9975, 0x9976, 0x9977, |
55 0x999A, 0x9971, 0x9972, 0x9973, 0x999B, 0x9975, 0x9976, 0x9977, |
51 0x9978, 0x999C, 0x999D, 0x997A, 0x99B3, 0x997B, 0x99B4, 0x99B5, |
56 0x9978, 0x999C, 0x999D, 0x997A, 0x99B3, 0x997B, 0x99B4, 0x99B5, |
52 0x997C, 0x997D, 0x997E, 0x999E, 0x999F, 0x9981, 0x99A0, 0x9983, |
57 0x997C, 0x997D, 0x997E, 0x999E, 0x999F, 0x9981, 0x99A0, 0x9983, |
53 0x9984, 0x99A1, 0x99B9, 0x99A2, 0x99A3, 0x99A4, 0x9988, 0x9989}; |
58 0x9984, 0x99A1, 0x99B9, 0x99A2, 0x99A3, 0x99A4, 0x9988, 0x9989}; |
54 |
59 |
55 const uint KDisableLocalAreaCoverageMask = 0x0800; |
60 const uint DISABLE_LOCAL_AREA_COVERAGE_MASK = 0x0800; |
56 |
61 |
57 const int KPsNameChangeThresholdSeconds = 10; |
62 const int PS_NAME_CHANGE_THRESHOLD_SECONDS = 10; |
63 |
|
64 // Macros to help protect shared null and manual station instances to help debugging |
|
65 // Enabled in emulator or win32 builds by default |
|
66 #if defined __WINS__ || defined BUILD_WIN32 |
|
67 # define PROTECT_SHAREDNULL_AND_MANUALSTATION |
|
68 #endif |
|
69 |
|
70 #ifdef PROTECT_SHAREDNULL_AND_MANUALSTATION |
|
71 # define ASSERT_SHARED_NULL_IS_INTACT \ |
|
72 Q_ASSERT_X( mData->mPresetIndex != SharedNull, "RadioStation", "Shared null modified illegally!" ); |
|
73 # define ASSERT_MANUAL_STATION \ |
|
74 Q_ASSERT_X( mData->mPresetIndex != ManualStation, "RadioStation", "Illegally modifying manual station" ); |
|
75 #else |
|
76 # define ASSERT_SHARED_NULL_IS_INTACT |
|
77 # define ASSERT_MANUAL_STATION |
|
78 #endif // PROTECT_SHAREDNULL_AND_MANUALSTATION |
|
58 |
79 |
59 /** |
80 /** |
60 * Static shared data instance that is used by all default-constructed RadioStation instances |
81 * Static shared data instance that is used by all default-constructed RadioStation instances |
61 */ |
82 */ |
62 Q_GLOBAL_STATIC_WITH_ARGS( RadioStationPrivate, shared_null, ( RadioStation::SharedNull ) ) |
83 static RadioStationPrivate shared_null( RadioStation::SharedNull, 0 ); |
63 |
84 |
64 /*! |
85 /*! |
65 * |
86 * |
66 */ |
87 */ |
67 QString RadioStation::parseFrequency( uint frequency ) |
88 QString RadioStation::parseFrequency( uint frequency ) |
68 { |
89 { |
69 QString freqString; |
90 QString freqString; |
70 freqString.sprintf( "%.1f", qreal( frequency ) / KFrequencyMultiplier ); |
91 freqString.sprintf( "%.1f", qreal( frequency ) / FREQUENCY_MULTIPLIER ); |
71 return freqString; |
92 return freqString; |
72 } |
93 } |
73 |
94 |
74 /*! |
95 /*! |
75 * |
96 * |
76 */ |
97 */ |
77 RadioStation::RadioStation() : |
98 RadioStation::RadioStation() : |
78 QObject( 0 ) |
99 mData( &shared_null ) |
79 { |
100 { |
80 mData = shared_null(); |
|
81 mData->ref.ref(); |
|
82 } |
101 } |
83 |
102 |
84 /*! |
103 /*! |
85 * |
104 * |
86 */ |
105 */ |
87 RadioStation::RadioStation( const RadioStation& other ) : |
106 RadioStation::RadioStation( const RadioStation& other ) : |
88 QObject( 0 ) |
107 mData( other.mData ) |
108 { |
|
109 } |
|
110 |
|
111 /*! |
|
112 * |
|
113 */ |
|
114 RadioStation::RadioStation( int presetIndex, uint frequency ) : |
|
115 mData( new RadioStationPrivate( presetIndex, frequency ) ) |
|
116 { |
|
117 } |
|
118 |
|
119 /*! |
|
120 * |
|
121 */ |
|
122 RadioStation::~RadioStation() |
|
123 { |
|
124 } |
|
125 |
|
126 /*! |
|
127 * |
|
128 */ |
|
129 RadioStation& RadioStation::operator=( const RadioStation& other ) |
|
89 { |
130 { |
90 mData = other.mData; |
131 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; |
132 return *this; |
118 } |
133 } |
119 |
134 |
120 /*! |
135 /*! |
121 * |
136 * |
122 */ |
137 */ |
123 void RadioStation::reset() |
138 void RadioStation::reset() |
124 { |
139 { |
125 decrementReferenceCount(); |
140 mData = &shared_null; |
126 mData = shared_null(); |
141 ASSERT_SHARED_NULL_IS_INTACT |
127 mData->ref.ref(); |
|
128 } |
142 } |
129 |
143 |
130 /*! |
144 /*! |
131 * |
145 * |
132 */ |
146 */ |
133 void RadioStation::setChangeFlags( RadioStation::Change flags ) |
147 void RadioStation::setChangeFlags( RadioStation::Change flags ) |
134 { |
148 { |
135 if ( mData->mChangeFlags != flags ) { |
149 if ( mData->mChangeFlags != flags ) { |
136 detach(); |
|
137 mData->mChangeFlags = flags; |
150 mData->mChangeFlags = flags; |
138 } |
151 } |
139 } |
152 } |
140 |
153 |
141 /*! |
154 /*! |
142 * |
155 * |
143 */ |
156 */ |
144 void RadioStation::setPresetIndex( int presetIndex ) |
157 void RadioStation::setPresetIndex( int presetIndex ) |
145 { |
158 { |
146 if ( mData->mPresetIndex != presetIndex ) { |
159 if ( mData->mPresetIndex != presetIndex ) { |
147 detach(); |
160 ASSERT_MANUAL_STATION |
148 mData->mPresetIndex = presetIndex; |
161 mData->mPresetIndex = presetIndex; |
149 mData->mChangeFlags |= RadioStation::PersistentDataChanged; |
162 mData->mChangeFlags |= RadioStation::PersistentDataChanged; |
150 } |
163 } |
151 } |
164 } |
152 |
165 |
154 * |
167 * |
155 */ |
168 */ |
156 void RadioStation::setFrequency( uint frequency ) |
169 void RadioStation::setFrequency( uint frequency ) |
157 { |
170 { |
158 if ( mData->mFrequency != frequency ) { |
171 if ( mData->mFrequency != frequency ) { |
159 detach(); |
|
160 mData->mFrequency = frequency; |
172 mData->mFrequency = frequency; |
161 mData->mChangeFlags |= RadioStation::PersistentDataChanged; |
173 mData->mChangeFlags |= RadioStation::PersistentDataChanged; |
174 ASSERT_SHARED_NULL_IS_INTACT |
|
162 } |
175 } |
163 } |
176 } |
164 |
177 |
165 /*! |
178 /*! |
166 * Sets the preset name |
179 * Sets the preset name |
168 void RadioStation::setName( const QString& name ) |
181 void RadioStation::setName( const QString& name ) |
169 { |
182 { |
170 // Name emptiness is checked because this name setter is used by incoming RDS PS name |
183 // Name emptiness is checked because this name setter is used by incoming RDS PS name |
171 // and empty names should be ignored |
184 // and empty names should be ignored |
172 if ( !name.isEmpty() && !mData->mRenamedByUser && mData->mName.compare( name ) != 0 ) { |
185 if ( !name.isEmpty() && !mData->mRenamedByUser && mData->mName.compare( name ) != 0 ) { |
173 detach(); |
|
174 mData->mName = name.trimmed(); |
186 mData->mName = name.trimmed(); |
175 mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::NameChanged; |
187 mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::NameChanged; |
176 |
188 |
177 // Save the time when PS name changed and if the last change was too close to the current time |
189 // 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. |
190 // change the PS type to dynamic if it has already been incorrectly determined to be static. |
179 QTime previousChange = mData->mLastPsNameChangeTime; |
191 QTime previousChange = mData->mLastPsNameChangeTime; |
180 mData->mLastPsNameChangeTime = QTime::currentTime(); |
192 mData->mLastPsNameChangeTime = QTime::currentTime(); |
181 if ( previousChange.isValid() && mData->mPsType == RadioStation::Static && |
193 if ( previousChange.isValid() && mData->mPsType == RadioStation::Static && |
182 previousChange.secsTo( mData->mLastPsNameChangeTime ) < KPsNameChangeThresholdSeconds ) { |
194 previousChange.secsTo( mData->mLastPsNameChangeTime ) < PS_NAME_CHANGE_THRESHOLD_SECONDS ) { |
183 LOG( "Station changed PS name too often. PS type changed to Dynamic" ); |
195 LOG( "Station changed PS name too often. PS type changed to Dynamic" ); |
184 mData->mPsType = RadioStation::Dynamic; |
196 mData->mPsType = RadioStation::Dynamic; |
185 mData->mDynamicPsText = mData->mName; |
197 mData->mDynamicPsText = mData->mName; |
186 mData->mName = ""; |
198 mData->mName = ""; |
187 mData->mChangeFlags |= RadioStation::PsTypeChanged | RadioStation::DynamicPsChanged; |
199 mData->mChangeFlags |= RadioStation::PsTypeChanged | RadioStation::DynamicPsChanged; |
188 mData->mCallSignCheckDone = false; |
200 mData->mCallSignCheckDone = false; |
189 } |
201 } |
190 |
202 |
191 //TODO: This is a temporary thing to see some URL. Remove this |
203 //TODO: This is a temporary thing to see some URL. Remove this |
192 if ( !mData->mName.isEmpty() ) { |
204 if ( !mData->mName.isEmpty() ) { |
193 mData->mUrl = "<a href=\"buu\">www." + mData->mName.toLower() + ".fi</a>"; |
205 QString url = mData->mName.toLower().remove( " " ); |
206 mData->mUrl = "www." + url + ".fi"; |
|
194 } else { |
207 } else { |
195 mData->mUrl = ""; |
208 mData->mUrl = ""; |
196 } |
209 } |
197 mData->mChangeFlags |= RadioStation::UrlChanged; |
210 mData->mChangeFlags |= RadioStation::UrlChanged; |
211 ASSERT_SHARED_NULL_IS_INTACT |
|
198 } |
212 } |
199 } |
213 } |
200 |
214 |
201 /*! |
215 /*! |
202 * |
216 * |
203 */ |
217 */ |
204 void RadioStation::setGenre( const int genre ) |
218 void RadioStation::setGenre( const int genre ) |
205 { |
219 { |
206 if ( mData->mGenre != genre ) { |
220 if ( mData->mGenre != genre ) { |
207 detach(); |
|
208 mData->mGenre = genre; |
221 mData->mGenre = genre; |
209 mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::GenreChanged; |
222 mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::GenreChanged; |
223 ASSERT_SHARED_NULL_IS_INTACT |
|
210 } |
224 } |
211 } |
225 } |
212 |
226 |
213 /*! |
227 /*! |
214 * |
228 * |
215 */ |
229 */ |
216 void RadioStation::setUrl( const QString& url ) |
230 void RadioStation::setUrl( const QString& url ) |
217 { |
231 { |
218 if ( mData->mUrl.compare( url ) != 0 ) { |
232 if ( mData->mUrl.compare( url ) != 0 ) { |
219 detach(); |
|
220 mData->mUrl = url; |
233 mData->mUrl = url; |
221 mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::UrlChanged; |
234 mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::UrlChanged; |
235 ASSERT_SHARED_NULL_IS_INTACT |
|
222 } |
236 } |
223 } |
237 } |
224 |
238 |
225 /*! |
239 /*! |
226 * Sets the PI code |
240 * Sets the PI code |
228 */ |
242 */ |
229 bool RadioStation::setPiCode( int piCode, RadioRegion::Region region ) |
243 bool RadioStation::setPiCode( int piCode, RadioRegion::Region region ) |
230 { |
244 { |
231 LOG_FORMAT( "RadioStation::setPiCode new PI: %d", piCode ); |
245 LOG_FORMAT( "RadioStation::setPiCode new PI: %d", piCode ); |
232 // toggling local area coverage bit code must not be interpreted as new PI code |
246 // toggling local area coverage bit code must not be interpreted as new PI code |
233 if( region != RadioRegion::America ) |
247 if ( region != RadioRegion::America ) { |
234 { |
248 piCode &= ~DISABLE_LOCAL_AREA_COVERAGE_MASK; |
235 piCode &= ~KDisableLocalAreaCoverageMask; |
|
236 } |
249 } |
237 |
250 |
238 LOG_FORMAT( "stored PI: %d", mData->mPiCode ); |
251 LOG_FORMAT( "stored PI: %d", mData->mPiCode ); |
239 LOG_FORMAT( "call sign check done: %d", mData->mCallSignCheckDone ); |
252 LOG_FORMAT( "call sign check done: %d", mData->mCallSignCheckDone ); |
240 //prevent executing the below code when unnessesary |
253 //prevent executing the below code when unnessesary |
241 if ( mData->mPiCode != piCode || !mData->mCallSignCheckDone ) |
254 if ( mData->mPiCode != piCode || !mData->mCallSignCheckDone ) { |
242 { |
|
243 detach(); |
|
244 mData->mPiCode = piCode; |
255 mData->mPiCode = piCode; |
245 mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::PiCodeChanged; |
256 mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::PiCodeChanged; |
246 // call sign not calculated for clear channel stations |
257 // call sign not calculated for clear channel stations |
247 //TODO: Remove magic numbers |
258 //TODO: Remove magic numbers |
248 if( ( (mData->mPiCode & 0xF000 ) >> 12 ) == 0x1 ) |
259 if ( ( (mData->mPiCode & 0xF000 ) >> 12 ) == 0x1 ) { |
249 { |
|
250 LOG( "Clear channel station" ); |
260 LOG( "Clear channel station" ); |
251 mData->mCallSignCheckDone = true; |
261 mData->mCallSignCheckDone = true; |
252 } |
262 } else if ( region == RadioRegion::America && mData->mName.isEmpty() && !isRenamed() ) { |
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" ); |
263 LOG( "Calculate call sign" ); |
257 mData->mName = piCodeToCallSign( mData->mPiCode ); |
264 mData->mName = piCodeToCallSign( mData->mPiCode ); |
258 mData->mChangeFlags |= RadioStation::NameChanged; |
265 mData->mChangeFlags |= RadioStation::NameChanged; |
259 } |
266 } |
260 |
267 |
261 if ( mData->mChangeFlags.testFlag( RadioStation::PsTypeChanged ) ) |
268 if ( mData->mChangeFlags.testFlag( RadioStation::PsTypeChanged ) ) { |
262 { |
|
263 LOG( "Call sign check done" ); |
269 LOG( "Call sign check done" ); |
264 mData->mCallSignCheckDone = true; |
270 mData->mCallSignCheckDone = true; |
265 } |
271 } |
266 |
272 |
273 ASSERT_SHARED_NULL_IS_INTACT |
|
267 return true; |
274 return true; |
268 } |
275 } |
269 return false; |
276 return false; |
270 } |
277 } |
271 |
278 |
273 * |
280 * |
274 */ |
281 */ |
275 void RadioStation::setPsType( PsType psType ) |
282 void RadioStation::setPsType( PsType psType ) |
276 { |
283 { |
277 if ( mData->mPsType != psType ) { |
284 if ( mData->mPsType != psType ) { |
278 detach(); |
|
279 mData->mPsType = psType; |
285 mData->mPsType = psType; |
280 mData->mChangeFlags |= RadioStation::PsTypeChanged; |
286 mData->mChangeFlags |= RadioStation::PsTypeChanged; |
287 ASSERT_SHARED_NULL_IS_INTACT |
|
281 } |
288 } |
282 } |
289 } |
283 |
290 |
284 /*! |
291 /*! |
285 * |
292 * |
286 */ |
293 */ |
287 void RadioStation::setRadioText( const QString& radioText ) |
294 void RadioStation::setRadioText( const QString& radioText ) |
288 { |
295 { |
289 if ( mData->mRadioText.compare( radioText ) != 0 ) { |
296 if ( mData->mRadioText.compare( radioText ) != 0 ) { |
290 detach(); |
|
291 mData->mRadioText = radioText.isEmpty() ? "" : radioText.trimmed(); |
297 mData->mRadioText = radioText.isEmpty() ? "" : radioText.trimmed(); |
292 mData->mChangeFlags |= RadioStation::RadioTextChanged; |
298 mData->mChangeFlags |= RadioStation::RadioTextChanged; |
299 ASSERT_SHARED_NULL_IS_INTACT |
|
293 } |
300 } |
294 } |
301 } |
295 |
302 |
296 /*! |
303 /*! |
297 * |
304 * |
306 if ( rtPlusClass == RtPlus::Homepage ) { |
313 if ( rtPlusClass == RtPlus::Homepage ) { |
307 setUrl( rtPlusItem ); |
314 setUrl( rtPlusItem ); |
308 return; |
315 return; |
309 } |
316 } |
310 |
317 |
311 detach(); |
318 const QString replacement = HTML_TAG_START + rtPlusItem + HTML_TAG_END; |
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 |
319 |
320 mData->mRadioText.replace( rtPlusItem, replacement ); |
320 mData->mRadioText.replace( rtPlusItem, replacement ); |
321 mData->mChangeFlags |= RadioStation::RadioTextChanged; |
321 mData->mChangeFlags |= RadioStation::RadioTextChanged; |
322 } |
322 } |
323 } |
323 } |
326 * |
326 * |
327 */ |
327 */ |
328 void RadioStation::setDynamicPsText( const QString& dynamicPsText ) |
328 void RadioStation::setDynamicPsText( const QString& dynamicPsText ) |
329 { |
329 { |
330 if ( mData->mDynamicPsText.compare( dynamicPsText ) != 0 ) { |
330 if ( mData->mDynamicPsText.compare( dynamicPsText ) != 0 ) { |
331 detach(); |
|
332 mData->mDynamicPsText = dynamicPsText; |
331 mData->mDynamicPsText = dynamicPsText; |
333 mData->mChangeFlags |= RadioStation::DynamicPsChanged; |
332 mData->mChangeFlags |= RadioStation::DynamicPsChanged; |
333 ASSERT_SHARED_NULL_IS_INTACT |
|
334 } |
334 } |
335 } |
335 } |
336 |
336 |
337 /*! |
337 /*! |
338 * |
338 * |
339 */ |
339 */ |
340 bool RadioStation::isValid() const |
340 bool RadioStation::isValid() const |
341 { |
341 { |
342 return mData->mPresetIndex >= 0 && mData->mFrequency > 0; |
342 return mData->mFrequency > 0 && ( isType( ManualStation ) || mData->mPresetIndex >= 0 ); |
343 } |
343 } |
344 |
344 |
345 /*! |
345 /*! |
346 * |
346 * |
347 */ |
347 */ |
356 void RadioStation::setUserDefinedName( const QString& name ) |
356 void RadioStation::setUserDefinedName( const QString& name ) |
357 { |
357 { |
358 // We don't check for name emptiness because this setter is used also to remove the renaming |
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 |
359 // of a station by setting an empty name |
360 if ( mData->mName.compare( name ) != 0 ) { |
360 if ( mData->mName.compare( name ) != 0 ) { |
361 detach(); |
|
362 mData->mName = name; |
361 mData->mName = name; |
363 mData->mRenamedByUser = !name.isEmpty(); |
362 mData->mRenamedByUser = !name.isEmpty(); |
364 mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::NameChanged; |
363 mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::NameChanged; |
365 } |
364 } |
366 } |
365 } |
382 } |
381 } |
383 |
382 |
384 /*! |
383 /*! |
385 * |
384 * |
386 */ |
385 */ |
387 QString RadioStation::frequencyMhz() const |
386 QString RadioStation::frequencyString() const |
388 { |
387 { |
389 return parseFrequency( mData->mFrequency ); |
388 return parseFrequency( mData->mFrequency ); |
390 } |
389 } |
391 |
390 |
392 /*! |
391 /*! |
409 * |
408 * |
410 */ |
409 */ |
411 void RadioStation::setFavorite( bool favorite ) |
410 void RadioStation::setFavorite( bool favorite ) |
412 { |
411 { |
413 if ( isFavorite() != favorite ) { |
412 if ( isFavorite() != favorite ) { |
414 detach(); |
|
415 favorite ? setType( RadioStation::Favorite ) : unsetType( RadioStation::Favorite ); |
413 favorite ? setType( RadioStation::Favorite ) : unsetType( RadioStation::Favorite ); |
416 mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::FavoriteChanged; |
414 mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::FavoriteChanged; |
415 ASSERT_SHARED_NULL_IS_INTACT |
|
417 } |
416 } |
418 } |
417 } |
419 |
418 |
420 /*! |
419 /*! |
421 * |
420 * |
434 } |
433 } |
435 |
434 |
436 /*! |
435 /*! |
437 * |
436 * |
438 */ |
437 */ |
439 bool RadioStation::hasPiCode() const |
438 int RadioStation::piCode() const |
440 { |
439 { |
441 return mData->mPiCode != -1; |
440 return mData->mPiCode; |
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 } |
441 } |
455 |
442 |
456 /*! |
443 /*! |
457 * |
444 * |
458 */ |
445 */ |
459 void RadioStation::setType( RadioStation::Type type ) |
446 void RadioStation::setType( RadioStation::Type type ) |
460 { |
447 { |
461 if ( !isType( type ) ) { |
448 if ( !isType( type ) ) { |
462 detach(); |
|
463 |
449 |
464 // Check if favorite-status changed |
450 // Check if favorite-status changed |
465 if ( mData->mType.testFlag( RadioStation::Favorite ) != type.testFlag( RadioStation::Favorite ) ) { |
451 if ( mData->mType.testFlag( RadioStation::Favorite ) != type.testFlag( RadioStation::Favorite ) ) { |
466 mData->mChangeFlags |= RadioStation::FavoriteChanged; |
452 mData->mChangeFlags |= RadioStation::FavoriteChanged; |
467 } |
453 } |
468 |
454 |
469 mData->mType |= type; |
455 mData->mType |= type; |
470 mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::TypeChanged; |
456 mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::TypeChanged; |
457 ASSERT_SHARED_NULL_IS_INTACT |
|
471 } |
458 } |
472 } |
459 } |
473 |
460 |
474 /*! |
461 /*! |
475 * |
462 * |
476 */ |
463 */ |
477 void RadioStation::unsetType( RadioStation::Type type ) |
464 void RadioStation::unsetType( RadioStation::Type type ) |
478 { |
465 { |
479 if ( isType( type ) ) { |
466 if ( isType( type ) ) { |
480 detach(); |
|
481 |
467 |
482 // Check if favorite-status changed |
468 // Check if favorite-status changed |
483 if ( mData->mType.testFlag( RadioStation::Favorite ) != type.testFlag( RadioStation::Favorite ) ) { |
469 if ( mData->mType.testFlag( RadioStation::Favorite ) != type.testFlag( RadioStation::Favorite ) ) { |
484 mData->mChangeFlags |= RadioStation::FavoriteChanged; |
470 mData->mChangeFlags |= RadioStation::FavoriteChanged; |
485 } |
471 } |
486 |
472 |
487 mData->mType &= ~type; |
473 mData->mType &= ~type; |
488 mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::TypeChanged; |
474 mData->mChangeFlags |= RadioStation::PersistentDataChanged | RadioStation::TypeChanged; |
475 ASSERT_SHARED_NULL_IS_INTACT |
|
489 } |
476 } |
490 } |
477 } |
491 |
478 |
492 /*! |
479 /*! |
493 * |
480 * |
549 * |
536 * |
550 */ |
537 */ |
551 void RadioStation::resetChangeFlags() |
538 void RadioStation::resetChangeFlags() |
552 { |
539 { |
553 if ( mData->mChangeFlags != RadioStation::NoChange ) { |
540 if ( mData->mChangeFlags != RadioStation::NoChange ) { |
554 detach(); |
|
555 mData->mChangeFlags = RadioStation::NoChange; |
541 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 } |
542 } |
568 } |
543 } |
569 |
544 |
570 /*! |
545 /*! |
571 * |
546 * |
574 { |
549 { |
575 QString callSign; |
550 QString callSign; |
576 |
551 |
577 LOG_FORMAT( "RadioStation::piCodeToCallSign PI: %d", programmeIdentification ); |
552 LOG_FORMAT( "RadioStation::piCodeToCallSign PI: %d", programmeIdentification ); |
578 // call signs beginning with 'K' |
553 // call signs beginning with 'K' |
579 if( ( programmeIdentification>=KKxxxCallSignPiFirst ) && ( programmeIdentification < KWxxxCallSignPiFirst ) ) { |
554 if ( ( programmeIdentification>=KXXX_CALLSIGN_PI_FIRST ) && ( programmeIdentification < WXXX_CALLSIGN_PI_FIRST ) ) { |
580 callSign += "K"; |
555 callSign += "K"; |
581 callSign += iterateCallSign( KKxxxCallSignPiFirst, programmeIdentification ); |
556 callSign += iterateCallSign( KXXX_CALLSIGN_PI_FIRST, programmeIdentification ); |
582 } |
557 } |
583 // call signs beginning with 'W' |
558 // call signs beginning with 'W' |
584 else if (( programmeIdentification >= KWxxxCallSignPiFirst ) && ( programmeIdentification <= KWxxxCallSignPiLast )) { |
559 else if ( ( programmeIdentification >= WXXX_CALLSIGN_PI_FIRST ) && ( programmeIdentification <= WXXX_CALLSIGN_PI_LAST ) ) { |
585 callSign += "W"; |
560 callSign += "W"; |
586 callSign += iterateCallSign( KWxxxCallSignPiFirst, programmeIdentification ); |
561 callSign += iterateCallSign( WXXX_CALLSIGN_PI_FIRST, programmeIdentification ); |
587 } |
562 } |
588 // 3 letter only call signs |
563 // 3 letter only call signs |
589 else if(( programmeIdentification >= KxxxCallSignPiFirst ) && ( programmeIdentification <= KxxxCallSignPiLast)) { |
564 else if ( ( programmeIdentification >= XXX_CALLSIGN_PI_FIRST ) && ( programmeIdentification <= XXX_CALLSIGN_PI_LAST ) ) { |
590 callSign += callSignString( programmeIdentification ); |
565 callSign += callSignString( programmeIdentification ); |
591 } |
566 } else { |
592 else |
|
593 { |
|
594 LOG( "RadioStation::piCodeToCallSign - Unhandled else" ); |
567 LOG( "RadioStation::piCodeToCallSign - Unhandled else" ); |
595 } |
568 } |
596 |
569 |
597 LOG_FORMAT( "RadioStation::piCodeToCallSign, call sign: %s", GETSTRING(callSign) ); |
570 LOG_FORMAT( "RadioStation::piCodeToCallSign, call sign: %s", GETSTRING(callSign) ); |
598 |
571 |
605 QString RadioStation::iterateCallSign( int piBase, int programmeIdentification ) |
578 QString RadioStation::iterateCallSign( int piBase, int programmeIdentification ) |
606 { |
579 { |
607 QString callSign; |
580 QString callSign; |
608 LOG_FORMAT( "RadioStation::iterateCallSign base: %d", piBase ); |
581 LOG_FORMAT( "RadioStation::iterateCallSign base: %d", piBase ); |
609 |
582 |
610 int sum(0), i(0); |
583 int sum = 0; |
611 |
584 int i = 0; |
612 while( sum < programmeIdentification ) { |
585 while( sum < programmeIdentification ) { |
613 i++; |
586 ++i; |
614 sum = piBase + i * 676 + 0 + 0; |
587 sum = piBase + i * 676 + 0 + 0; |
615 } |
588 } |
616 callSign += callSignChar( i - 1 ); |
589 callSign += callSignChar( i - 1 ); |
617 |
590 |
618 int tmpSum( sum - 676 ); |
591 int tmpSum( sum - 676 ); |
619 sum -= 676; |
592 sum -= 676; |
620 i = 0; |
593 i = 0; |
621 while( sum <= programmeIdentification ) { |
594 while( sum <= programmeIdentification ) { |
622 i++; |
595 ++i; |
623 sum = tmpSum + 0 + i * 26 + 0; |
596 sum = tmpSum + 0 + i * 26 + 0; |
624 } |
597 } |
625 callSign += callSignChar( i - 1 ); |
598 callSign += callSignChar( i - 1 ); |
626 |
599 |
627 tmpSum = sum - 26; |
600 tmpSum = sum - 26; |
628 sum -= 26; |
601 sum -= 26; |
629 i = 0; |
602 i = 0; |
630 while( sum <= programmeIdentification ) { |
603 while( sum <= programmeIdentification ) { |
631 i++; |
604 ++i; |
632 sum = tmpSum + 0 + 0 + i; |
605 sum = tmpSum + 0 + 0 + i; |
633 } |
606 } |
634 callSign += callSignChar( i - 1 ); |
607 callSign += callSignChar( i - 1 ); |
635 |
608 |
636 return callSign; |
609 return callSign; |
639 /*! |
612 /*! |
640 * |
613 * |
641 */ |
614 */ |
642 QString RadioStation::callSignString( uint programmeIdentification ) |
615 QString RadioStation::callSignString( uint programmeIdentification ) |
643 { |
616 { |
644 for ( uint i = 0; i < KThreeLetterCallSignCount; ++i ) { |
617 for ( uint i = 0; i < THREE_LETTER_CALLSIGN_COUNT; ++i ) { |
645 if( piCode[i] == programmeIdentification ) { |
618 if( PI_CODE_TABLE[i] == programmeIdentification ) { |
646 return callSign[i]; |
619 return CALLSIGN_TABLE[i]; |
647 } |
620 } |
648 } |
621 } |
649 |
622 |
650 LOG_FORMAT( "RadioStation::callSignString, Not found PI: %d", programmeIdentification ); |
623 LOG_FORMAT( "RadioStation::callSignString, Not found PI: %d", programmeIdentification ); |
651 |
624 |
652 return QString("????"); |
625 return QString( "????" ); |
653 } |
626 } |
654 |
627 |
655 /*! |
628 /*! |
656 * |
629 * |
657 */ |
630 */ |
658 char RadioStation::callSignChar( uint decimalValue ) |
631 char RadioStation::callSignChar( uint decimalValue ) |
659 { |
632 { |
660 LOG_FORMAT( "RadioStation::callSignChar A+: %d", decimalValue ); |
633 LOG_FORMAT( "RadioStation::callSignChar A+: %d", decimalValue ); |
661 if ( decimalValue <= KLastCallSignCharCode ) { |
634 if ( decimalValue <= LAST_CALLSIGN_CHAR_CODE ) { |
662 return static_cast<char>( 'A' + decimalValue ); |
635 return static_cast<char>( 'A' + decimalValue ); |
663 } |
636 } |
664 return '?'; |
637 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 } |
638 } |
690 |
639 |
691 /** |
640 /** |
692 * Checks if the class is detached from implicitly shared data |
641 * Checks if the class is detached from implicitly shared data |
693 */ |
642 */ |