1 /******************************************************************************\
2 * Copyright (c) 2004-2020
3 *
4 * Author(s):
5 * Volker Fischer
6 *
7 ******************************************************************************
8 *
9 * This program is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License as published by the Free Software
11 * Foundation; either version 2 of the License, or (at your option) any later
12 * version.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 *
23 \******************************************************************************/
24
25 #include "util.h"
26 #include "client.h"
27
28 /* Implementation *************************************************************/
29 // Input level meter implementation --------------------------------------------
Update(const CVector<short> & vecsAudio,const int iMonoBlockSizeSam,const bool bIsStereoIn)30 void CStereoSignalLevelMeter::Update ( const CVector<short>& vecsAudio, const int iMonoBlockSizeSam, const bool bIsStereoIn )
31 {
32 // Get maximum of current block
33 //
34 // Speed optimization:
35 // - we only make use of the negative values and ignore the positive ones (since
36 // int16 has range {-32768, 32767}) -> we do not need to call the fabs() function
37 // - we only evaluate every third sample
38 //
39 // With these speed optimizations we might loose some information in
40 // special cases but for the average music signals the following code
41 // should give good results.
42 short sMinLOrMono = 0;
43 short sMinR = 0;
44
45 if ( bIsStereoIn )
46 {
47 // stereo in
48 for ( int i = 0; i < 2 * iMonoBlockSizeSam; i += 6 ) // 2 * 3 = 6 -> stereo
49 {
50 // left (or mono) and right channel
51 sMinLOrMono = std::min ( sMinLOrMono, vecsAudio[i] );
52 sMinR = std::min ( sMinR, vecsAudio[i + 1] );
53 }
54
55 // in case of mono out use minimum of both channels
56 if ( !bIsStereoOut )
57 {
58 sMinLOrMono = std::min ( sMinLOrMono, sMinR );
59 }
60 }
61 else
62 {
63 // mono in
64 for ( int i = 0; i < iMonoBlockSizeSam; i += 3 )
65 {
66 sMinLOrMono = std::min ( sMinLOrMono, vecsAudio[i] );
67 }
68 }
69
70 // apply smoothing, if in stereo out mode, do this for two channels
71 dCurLevelLOrMono = UpdateCurLevel ( dCurLevelLOrMono, -sMinLOrMono );
72
73 if ( bIsStereoOut )
74 {
75 dCurLevelR = UpdateCurLevel ( dCurLevelR, -sMinR );
76 }
77 }
78
UpdateCurLevel(double dCurLevel,const double dMax)79 double CStereoSignalLevelMeter::UpdateCurLevel ( double dCurLevel, const double dMax )
80 {
81 // decrease max with time
82 if ( dCurLevel >= METER_FLY_BACK )
83 {
84 dCurLevel *= dSmoothingFactor;
85 }
86 else
87 {
88 dCurLevel = 0;
89 }
90
91 // update current level -> only use maximum
92 if ( dMax > dCurLevel )
93 {
94 return dMax;
95 }
96 else
97 {
98 return dCurLevel;
99 }
100 }
101
CalcLogResultForMeter(const double & dLinearLevel)102 double CStereoSignalLevelMeter::CalcLogResultForMeter ( const double& dLinearLevel )
103 {
104 const double dNormLevel = dLinearLevel / _MAXSHORT;
105
106 // logarithmic measure
107 double dLevelForMeterdB = -100000.0; // large negative value
108
109 if ( dNormLevel > 0 )
110 {
111 dLevelForMeterdB = 20.0 * log10 ( dNormLevel );
112 }
113
114 // map to signal level meter (linear transformation of the input
115 // level range to the level meter range)
116 dLevelForMeterdB -= LOW_BOUND_SIG_METER;
117 dLevelForMeterdB *= NUM_STEPS_LED_BAR / ( UPPER_BOUND_SIG_METER - LOW_BOUND_SIG_METER );
118
119 if ( dLevelForMeterdB < 0 )
120 {
121 dLevelForMeterdB = 0;
122 }
123
124 return dLevelForMeterdB;
125 }
126
127 // CRC -------------------------------------------------------------------------
Reset()128 void CCRC::Reset()
129 {
130 // init state shift-register with ones. Set all registers to "1" with
131 // bit-wise not operation
132 iStateShiftReg = ~uint32_t ( 0 );
133 }
134
AddByte(const uint8_t byNewInput)135 void CCRC::AddByte ( const uint8_t byNewInput )
136 {
137 for ( int i = 0; i < 8; i++ )
138 {
139 // shift bits in shift-register for transition
140 iStateShiftReg <<= 1;
141
142 // take bit, which was shifted out of the register-size and place it
143 // at the beginning (LSB)
144 // (If condition is not satisfied, implicitly a "0" is added)
145 if ( ( iStateShiftReg & iBitOutMask ) > 0 )
146 {
147 iStateShiftReg |= 1;
148 }
149
150 // add new data bit to the LSB
151 if ( ( byNewInput & ( 1 << ( 8 - i - 1 ) ) ) > 0 )
152 {
153 iStateShiftReg ^= 1;
154 }
155
156 // add mask to shift-register if first bit is true
157 if ( iStateShiftReg & 1 )
158 {
159 iStateShiftReg ^= iPoly;
160 }
161 }
162 }
163
GetCRC()164 uint32_t CCRC::GetCRC()
165 {
166 // return inverted shift-register (1's complement)
167 iStateShiftReg = ~iStateShiftReg;
168
169 // remove bit which where shifted out of the shift-register frame
170 return iStateShiftReg & ( iBitOutMask - 1 );
171 }
172
173 /******************************************************************************\
174 * Audio Reverberation *
175 \******************************************************************************/
176 /*
177 The following code is based on "JCRev: John Chowning's reverberator class"
178 by Perry R. Cook and Gary P. Scavone, 1995 - 2004
179 which is in "The Synthesis ToolKit in C++ (STK)"
180 http://ccrma.stanford.edu/software/stk
181
182 Original description:
183 This class is derived from the CLM JCRev function, which is based on the use
184 of networks of simple allpass and comb delay filters. This class implements
185 three series allpass units, followed by four parallel comb filters, and two
186 decorrelation delay lines in parallel at the output.
187 */
Init(const EAudChanConf eNAudioChannelConf,const int iNStereoBlockSizeSam,const int iSampleRate,const float fT60)188 void CAudioReverb::Init ( const EAudChanConf eNAudioChannelConf, const int iNStereoBlockSizeSam, const int iSampleRate, const float fT60 )
189 {
190 // store parameters
191 eAudioChannelConf = eNAudioChannelConf;
192 iStereoBlockSizeSam = iNStereoBlockSizeSam;
193
194 // delay lengths for 44100 Hz sample rate
195 int lengths[9] = { 1116, 1356, 1422, 1617, 225, 341, 441, 211, 179 };
196 const float scaler = static_cast<float> ( iSampleRate ) / 44100.0f;
197
198 if ( scaler != 1.0f )
199 {
200 for ( int i = 0; i < 9; i++ )
201 {
202 int delay = static_cast<int> ( floorf ( scaler * lengths[i] ) );
203
204 if ( ( delay & 1 ) == 0 )
205 {
206 delay++;
207 }
208
209 while ( !isPrime ( delay ) )
210 {
211 delay += 2;
212 }
213
214 lengths[i] = delay;
215 }
216 }
217
218 for ( int i = 0; i < 3; i++ )
219 {
220 allpassDelays[i].Init ( lengths[i + 4] );
221 }
222
223 for ( int i = 0; i < 4; i++ )
224 {
225 combDelays[i].Init ( lengths[i] );
226 combFilters[i].setPole ( 0.2f );
227 }
228
229 setT60 ( fT60, iSampleRate );
230 outLeftDelay.Init ( lengths[7] );
231 outRightDelay.Init ( lengths[8] );
232 allpassCoefficient = 0.7f;
233 Clear();
234 }
235
isPrime(const int number)236 bool CAudioReverb::isPrime ( const int number )
237 {
238 /*
239 Returns true if argument value is prime. Taken from "class Effect" in
240 "STK abstract effects parent class".
241 */
242 if ( number == 2 )
243 {
244 return true;
245 }
246
247 if ( number & 1 )
248 {
249 for ( int i = 3; i < static_cast<int> ( sqrtf ( static_cast<float> ( number ) ) ) + 1; i += 2 )
250 {
251 if ( ( number % i ) == 0 )
252 {
253 return false;
254 }
255 }
256
257 return true; // prime
258 }
259 else
260 {
261 return false; // even
262 }
263 }
264
Clear()265 void CAudioReverb::Clear()
266 {
267 // reset and clear all internal state
268 allpassDelays[0].Reset ( 0 );
269 allpassDelays[1].Reset ( 0 );
270 allpassDelays[2].Reset ( 0 );
271 combDelays[0].Reset ( 0 );
272 combDelays[1].Reset ( 0 );
273 combDelays[2].Reset ( 0 );
274 combDelays[3].Reset ( 0 );
275 combFilters[0].Reset();
276 combFilters[1].Reset();
277 combFilters[2].Reset();
278 combFilters[3].Reset();
279 outRightDelay.Reset ( 0 );
280 outLeftDelay.Reset ( 0 );
281 }
282
setT60(const float fT60,const int iSampleRate)283 void CAudioReverb::setT60 ( const float fT60, const int iSampleRate )
284 {
285 // set the reverberation T60 decay time
286 for ( int i = 0; i < 4; i++ )
287 {
288 combCoefficient[i] = powf ( 10.0f, static_cast<float> ( -3.0f * combDelays[i].Size() / ( fT60 * iSampleRate ) ) );
289 }
290 }
291
setPole(const float fPole)292 void CAudioReverb::COnePole::setPole ( const float fPole )
293 {
294 // calculate IIR filter coefficients based on the pole value
295 fA = -fPole;
296 fB = 1.0f - fPole;
297 }
298
Calc(const float fIn)299 float CAudioReverb::COnePole::Calc ( const float fIn )
300 {
301 // calculate IIR filter
302 fLastSample = fB * fIn - fA * fLastSample;
303
304 return fLastSample;
305 }
306
Process(CVector<int16_t> & vecsStereoInOut,const bool bReverbOnLeftChan,const float fAttenuation)307 void CAudioReverb::Process ( CVector<int16_t>& vecsStereoInOut, const bool bReverbOnLeftChan, const float fAttenuation )
308 {
309 float fMixedInput, temp, temp0, temp1, temp2;
310
311 for ( int i = 0; i < iStereoBlockSizeSam; i += 2 )
312 {
313 // we sum up the stereo input channels (in case mono input is used, a zero
314 // shall be input for the right channel)
315 if ( eAudioChannelConf == CC_STEREO )
316 {
317 fMixedInput = 0.5f * ( vecsStereoInOut[i] + vecsStereoInOut[i + 1] );
318 }
319 else
320 {
321 if ( bReverbOnLeftChan )
322 {
323 fMixedInput = vecsStereoInOut[i];
324 }
325 else
326 {
327 fMixedInput = vecsStereoInOut[i + 1];
328 }
329 }
330
331 temp = allpassDelays[0].Get();
332 temp0 = allpassCoefficient * temp;
333 temp0 += fMixedInput;
334 allpassDelays[0].Add ( temp0 );
335 temp0 = -( allpassCoefficient * temp0 ) + temp;
336
337 temp = allpassDelays[1].Get();
338 temp1 = allpassCoefficient * temp;
339 temp1 += temp0;
340 allpassDelays[1].Add ( temp1 );
341 temp1 = -( allpassCoefficient * temp1 ) + temp;
342
343 temp = allpassDelays[2].Get();
344 temp2 = allpassCoefficient * temp;
345 temp2 += temp1;
346 allpassDelays[2].Add ( temp2 );
347 temp2 = -( allpassCoefficient * temp2 ) + temp;
348
349 const float temp3 = temp2 + combFilters[0].Calc ( combCoefficient[0] * combDelays[0].Get() );
350 const float temp4 = temp2 + combFilters[1].Calc ( combCoefficient[1] * combDelays[1].Get() );
351 const float temp5 = temp2 + combFilters[2].Calc ( combCoefficient[2] * combDelays[2].Get() );
352 const float temp6 = temp2 + combFilters[3].Calc ( combCoefficient[3] * combDelays[3].Get() );
353
354 combDelays[0].Add ( temp3 );
355 combDelays[1].Add ( temp4 );
356 combDelays[2].Add ( temp5 );
357 combDelays[3].Add ( temp6 );
358
359 const float filtout = temp3 + temp4 + temp5 + temp6;
360
361 outLeftDelay.Add ( filtout );
362 outRightDelay.Add ( filtout );
363
364 // inplace apply the attenuated reverb signal (for stereo always apply
365 // reverberation effect on both channels)
366 if ( ( eAudioChannelConf == CC_STEREO ) || bReverbOnLeftChan )
367 {
368 vecsStereoInOut[i] = Float2Short ( ( 1.0f - fAttenuation ) * vecsStereoInOut[i] + 0.5f * fAttenuation * outLeftDelay.Get() );
369 }
370
371 if ( ( eAudioChannelConf == CC_STEREO ) || !bReverbOnLeftChan )
372 {
373 vecsStereoInOut[i + 1] = Float2Short ( ( 1.0f - fAttenuation ) * vecsStereoInOut[i + 1] + 0.5f * fAttenuation * outRightDelay.Get() );
374 }
375 }
376 }
377
378 /******************************************************************************\
379 * GUI Utilities *
380 \******************************************************************************/
381 // About dialog ----------------------------------------------------------------
382 #ifndef HEADLESS
CAboutDlg(QWidget * parent)383 CAboutDlg::CAboutDlg ( QWidget* parent ) : CBaseDlg ( parent )
384 {
385 setupUi ( this );
386
387 // general description of software
388 txvAbout->setText ( "<p>" +
389 tr ( "This app enables musicians to perform real-time jam sessions "
390 "over the internet." ) +
391 "<br>" +
392 tr ( "There is a server which collects "
393 " the audio data from each client, mixes the audio data and sends the mix "
394 " back to each client." ) +
395 "</p>"
396 "<p><font face=\"courier\">" // GPL header text
397 "This program is free software; you can redistribute it and/or modify "
398 "it under the terms of the GNU General Public License as published by "
399 "the Free Software Foundation; either version 2 of the License, or "
400 "(at your option) any later version.<br>This program is distributed in "
401 "the hope that it will be useful, but WITHOUT ANY WARRANTY; without "
402 "even the implied warranty of MERCHANTABILITY or FITNESS FOR A "
403 "PARTICULAR PURPOSE. See the GNU General Public License for more "
404 "details.<br>You should have received a copy of the GNU General Public "
405 "License along with his program; if not, write to the Free Software "
406 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 "
407 "USA"
408 "</font></p>" );
409
410 // libraries used by this compilation
411 txvLibraries->setText (
412 tr ( "This app uses the following libraries, resources or code snippets:" ) + "<br><p>" + tr ( "Qt cross-platform application framework" ) +
413 ", <i><a href=\"http://www.qt.io\">http://www.qt.io</a></i></p>"
414 "<p>Opus Interactive Audio Codec"
415 ", <i><a href=\"http://www.opus-codec.org\">http://www.opus-codec.org</a></i></p>"
416 "<p>" +
417 tr ( "Audio reverberation code by Perry R. Cook and Gary P. Scavone" ) +
418 ", 1995 - 2004, <i><a href=\"http://ccrma.stanford.edu/software/stk\">"
419 "The Synthesis ToolKit in C++ (STK)</a></i></p>"
420 "<p>" +
421 tr ( "Some pixmaps are from the" ) +
422 " Open Clip Art Library (OCAL), "
423 "<i><a href=\"http://openclipart.org\">http://openclipart.org</a></i></p>"
424 "<p>" +
425 tr ( "Country flag icons by Mark James" ) + ", <i><a href=\"http://www.famfamfam.com\">http://www.famfamfam.com</a></i></p>" );
426
427 // contributors list
428 txvContributors->setText ( "<p>Volker Fischer (<a href=\"https://github.com/corrados\">corrados</a>)</p>"
429 "<p>Peter L. Jones (<a href=\"https://github.com/pljones\">pljones</a>)</p>"
430 "<p>Jonathan Baker-Bates (<a href=\"https://github.com/gilgongo\">gilgongo</a>)</p>"
431 "<p>ann0see (<a href=\"https://github.com/ann0see\">ann0see</a>)</p>"
432 "<p>Daniele Masato (<a href=\"https://github.com/doloopuntil\">doloopuntil</a>)</p>"
433 "<p>Martin Schilde (<a href=\"https://github.com/geheimerEichkater\">geheimerEichkater</a>)</p>"
434 "<p>Simon Tomlinson (<a href=\"https://github.com/sthenos\">sthenos</a>)</p>"
435 "<p>Marc jr. Landolt (<a href=\"https://github.com/braindef\">braindef</a>)</p>"
436 "<p>Olivier Humbert (<a href=\"https://github.com/trebmuh\">trebmuh</a>)</p>"
437 "<p>Tarmo Johannes (<a href=\"https://github.com/tarmoj\">tarmoj</a>)</p>"
438 "<p>mirabilos (<a href=\"https://github.com/mirabilos\">mirabilos</a>)</p>"
439 "<p>Hector Martin (<a href=\"https://github.com/marcan\">marcan</a>)</p>"
440 "<p>newlaurent62 (<a href=\"https://github.com/newlaurent62\">newlaurent62</a>)</p>"
441 "<p>AronVietti (<a href=\"https://github.com/AronVietti\">AronVietti</a>)</p>"
442 "<p>Emlyn Bolton (<a href=\"https://github.com/emlynmac\">emlynmac</a>)</p>"
443 "<p>Jos van den Oever (<a href=\"https://github.com/vandenoever\">vandenoever</a>)</p>"
444 "<p>Tormod Volden (<a href=\"https://github.com/tormodvolden\">tormodvolden</a>)</p>"
445 "<p>Alberstein8 (<a href=\"https://github.com/Alberstein8\">Alberstein8</a>)</p>"
446 "<p>Gauthier Fleutot Östervall (<a href=\"https://github.com/fleutot\">fleutot</a>)</p>"
447 "<p>Tony Mountifield (<a href=\"https://github.com/softins\">softins</a>)</p>"
448 "<p>HPS (<a href=\"https://github.com/hselasky\">hselasky</a>)</p>"
449 "<p>Stanislas Michalak (<a href=\"https://github.com/stanislas-m\">stanislas-m</a>)</p>"
450 "<p>JP Cimalando (<a href=\"https://github.com/jpcima\">jpcima</a>)</p>"
451 "<p>Adam Sampson (<a href=\"https://github.com/atsampson\">atsampson</a>)</p>"
452 "<p>Jakob Jarmar (<a href=\"https://github.com/jarmar\">jarmar</a>)</p>"
453 "<p>Stefan Weil (<a href=\"https://github.com/stweil\">stweil</a>)</p>"
454 "<p>Nils Brederlow (<a href=\"https://github.com/dingodoppelt\">dingodoppelt</a>)</p>"
455 "<p>Sebastian Krzyszkowiak (<a href=\"https://github.com/dos1\">dos1</a>)</p>"
456 "<p>Bryan Flamig (<a href=\"https://github.com/bflamig\">bflamig</a>)</p>"
457 "<p>Kris Raney (<a href=\"https://github.com/kraney\">kraney</a>)</p>"
458 "<p>dszgit (<a href=\"https://github.com/dszgit\">dszgit</a>)</p>"
459 "<p>nefarius2001 (<a href=\"https://github.com/nefarius2001\">nefarius2001</a>)</p>"
460 "<p>jc-Rosichini (<a href=\"https://github.com/jc-Rosichini\">jc-Rosichini</a>)</p>"
461 "<p>Julian Santander (<a href=\"https://github.com/j-santander\">j-santander</a>)</p>"
462 "<p>chigkim (<a href=\"https://github.com/chigkim\">chigkim</a>)</p>"
463 "<p>Bodo (<a href=\"https://github.com/bomm\">bomm</a>)</p>"
464 "<p>Christian Hoffmann (<a href=\"https://github.com/hoffie\">hoffie</a>)</p>"
465 "<p>jp8 (<a href=\"https://github.com/jp8\">jp8</a>)</p>"
466 "<p>James (<a href=\"https://github.com/jdrage\">jdrage</a>)</p>"
467 "<p>ranfdev (<a href=\"https://github.com/ranfdev\">ranfdev</a>)</p>"
468 "<p>bspeer (<a href=\"https://github.com/bspeer\">bspeer</a>)</p>"
469 "<p>Martin Passing (<a href=\"https://github.com/passing\">passing</a>)</p>"
470 "<p>DonC (<a href=\"https://github.com/dcorson-ticino-com\">dcorson-ticino-com</a>)</p>"
471 "<p>David Kastrup (<a href=\"https://github.com/dakhubgit\">dakhubgit</a>)</p>"
472 "<p>Jordan Lum (<a href=\"https://github.com/mulyaj\">mulyaj</a>)</p>"
473 "<p>Noam Postavsky (<a href=\"https://github.com/npostavs\">npostavs</a>)</p>"
474 "<p>David Savinkoff (<a href=\"https://github.com/DavidSavinkoff\">DavidSavinkoff</a>)</p>"
475 "<p>Johannes Brauers (<a href=\"https://github.com/JohannesBrx\">JohannesBrx</a>)</p>"
476 "<p>Henk De Groot (<a href=\"https://github.com/henkdegroot\">henkdegroot</a>)</p>"
477 "<p>Ferenc Wágner (<a href=\"https://github.com/wferi\">wferi</a>)</p>"
478 "<p>Martin Kaistra (<a href=\"https://github.com/djfun\">djfun</a>)</p>"
479 "<p>Burkhard Volkemer (<a href=\"https://github.com/buv\">buv</a>)</p>"
480 "<p>Magnus Groß (<a href=\"https://github.com/vimpostor\">vimpostor</a>)</p>"
481 "<p>Julien Taverna (<a href=\"https://github.com/jujudusud\">jujudusud</a>)</p>"
482 "<p>Detlef Hennings (<a href=\"https://github.com/DetlefHennings\">DetlefHennings</a>)</p>"
483 "<p>drummer1154 (<a href=\"https://github.com/drummer1154\">drummer1154</a>)</p>"
484 "<p>helgeerbe (<a href=\"https://github.com/helgeerbe\">helgeerbe</a>)</p>"
485 "<p>Hk1020 (<a href=\"https://github.com/Hk1020\">Hk1020</a>)</p>"
486 "<p>Jeroen van Veldhuizen (<a href=\"https://github.com/jeroenvv\">jeroenvv</a>)</p>"
487 "<p>Reinhard (<a href=\"https://github.com/reinhardwh\">reinhardwh</a>)</p>"
488 "<p>Stefan Menzel (<a href=\"https://github.com/menzels\">menzels</a>)</p>"
489 "<p>Dau Huy Ngoc (<a href=\"https://github.com/ngocdh\">ngocdh</a>)</p>"
490 "<p>Jiri Popek (<a href=\"https://github.com/jardous\">jardous</a>)</p>"
491 "<p>Gary Wang (<a href=\"https://github.com/BLumia\">BLumia</a>)</p>"
492 "<br>" +
493 tr ( "For details on the contributions check out the " ) +
494 "<a href=\"https://github.com/jamulussoftware/jamulus/graphs/contributors\">" + tr ( "Github Contributors list" ) +
495 "</a>." );
496
497 // translators
498 txvTranslation->setText ( "<p><b>" + tr ( "Spanish" ) +
499 "</b></p>"
500 "<p>Daryl Hanlon (<a href=\"https://github.com/ignotus666\">ignotus666</a>)</p>"
501 "<p><b>" +
502 tr ( "French" ) +
503 "</b></p>"
504 "<p>Olivier Humbert (<a href=\"https://github.com/trebmuh\">trebmuh</a>)</p>"
505 "<p>Julien Taverna (<a href=\"https://github.com/jujudusud\">jujudusud</a>)</p>"
506 "<p><b>" +
507 tr ( "Portuguese" ) +
508 "</b></p>"
509 "<p>Miguel de Matos (<a href=\"https://github.com/Snayler\">Snayler</a>)</p>"
510 "<p>Melcon Moraes (<a href=\"https://github.com/melcon\">melcon</a>)</p>"
511 "<p><b>" +
512 tr ( "Dutch" ) +
513 "</b></p>"
514 "<p>Jeroen Geertzen (<a href=\"https://github.com/jerogee\">jerogee</a>)</p>"
515 "<p>Henk De Groot (<a href=\"https://github.com/henkdegroot\">henkdegroot</a>)</p>"
516 "<p><b>" +
517 tr ( "Italian" ) +
518 "</b></p>"
519 "<p>Giuseppe Sapienza (<a href=\"https://github.com/dzpex\">dzpex</a>)</p>"
520 "<p><b>" +
521 tr ( "German" ) +
522 "</b></p>"
523 "<p>Volker Fischer (<a href=\"https://github.com/corrados\">corrados</a>)</p>"
524 "<p>Roland Moschel (<a href=\"https://github.com/rolamos\">rolamos</a>)</p>"
525 "<p><b>" +
526 tr ( "Polish" ) +
527 "</b></p>"
528 "<p>Martyna Danysz (<a href=\"https://github.com/Martyna27\">Martyna27</a>)</p>"
529 "<p>Tomasz Bojczuk (<a href=\"https://github.com/SeeLook\">SeeLook</a>)</p>"
530 "<p><b>" +
531 tr ( "Swedish" ) +
532 "</b></p>"
533 "<p>Daniel (<a href=\"https://github.com/genesisproject2020\">genesisproject2020</a>)</p>"
534 "<p><b>" +
535 tr ( "Slovak" ) +
536 "</b></p>"
537 "<p>Jose Riha (<a href=\"https://github.com/jose1711\">jose1711</a>)</p>" +
538 "<p><b>" + tr ( "Simplified Chinese" ) +
539 "</b></p>"
540 "<p>Gary Wang (<a href=\"https://github.com/BLumia\">BLumia</a>)</p>" );
541
542 // set version number in about dialog
543 lblVersion->setText ( GetVersionAndNameStr() );
544
545 // set window title
546 setWindowTitle ( tr ( "About " ) + APP_NAME );
547 }
548
549 // Licence dialog --------------------------------------------------------------
CLicenceDlg(QWidget * parent)550 CLicenceDlg::CLicenceDlg ( QWidget* parent ) : CBaseDlg ( parent )
551 {
552 /*
553 The licence dialog is structured as follows:
554 - text box with the licence text on the top
555 - check box: I &agree to the above licence terms
556 - Accept button (disabled if check box not checked)
557 - Decline button
558 */
559 setWindowIcon ( QIcon ( QString::fromUtf8 ( ":/png/main/res/fronticon.png" ) ) );
560
561 QVBoxLayout* pLayout = new QVBoxLayout ( this );
562 QHBoxLayout* pSubLayout = new QHBoxLayout;
563 QLabel* lblLicence =
564 new QLabel ( tr ( "This server requires you accept conditions before you can join. Please read these in the chat window." ), this );
565 QCheckBox* chbAgree = new QCheckBox ( tr ( "I have read the conditions and &agree." ), this );
566 butAccept = new QPushButton ( tr ( "Accept" ), this );
567 QPushButton* butDecline = new QPushButton ( tr ( "Decline" ), this );
568
569 pSubLayout->addStretch();
570 pSubLayout->addWidget ( chbAgree );
571 pSubLayout->addWidget ( butAccept );
572 pSubLayout->addWidget ( butDecline );
573 pLayout->addWidget ( lblLicence );
574 pLayout->addLayout ( pSubLayout );
575
576 // set some properties
577 butAccept->setEnabled ( false );
578 butAccept->setDefault ( true );
579
580 QObject::connect ( chbAgree, &QCheckBox::stateChanged, this, &CLicenceDlg::OnAgreeStateChanged );
581
582 QObject::connect ( butAccept, &QPushButton::clicked, this, &CLicenceDlg::accept );
583
584 QObject::connect ( butDecline, &QPushButton::clicked, this, &CLicenceDlg::reject );
585 }
586
587 // Help menu -------------------------------------------------------------------
CHelpMenu(const bool bIsClient,QWidget * parent)588 CHelpMenu::CHelpMenu ( const bool bIsClient, QWidget* parent ) : QMenu ( tr ( "&Help" ), parent )
589 {
590 QAction* pAction;
591
592 // standard help menu consists of about and what's this help
593 if ( bIsClient )
594 {
595 addAction ( tr ( "Getting &Started..." ), this, SLOT ( OnHelpClientGetStarted() ) );
596 addAction ( tr ( "Software &Manual..." ), this, SLOT ( OnHelpSoftwareMan() ) );
597 }
598 else
599 {
600 addAction ( tr ( "Getting &Started..." ), this, SLOT ( OnHelpServerGetStarted() ) );
601 }
602 addSeparator();
603 addAction ( tr ( "What's &This" ), this, SLOT ( OnHelpWhatsThis() ), QKeySequence ( Qt::SHIFT + Qt::Key_F1 ) );
604 addSeparator();
605 pAction = addAction ( tr ( "&About Jamulus..." ), this, SLOT ( OnHelpAbout() ) );
606 pAction->setMenuRole ( QAction::AboutRole ); // required for Mac
607 pAction = addAction ( tr ( "About &Qt..." ), this, SLOT ( OnHelpAboutQt() ) );
608 pAction->setMenuRole ( QAction::AboutQtRole ); // required for Mac
609 }
610
611 // Language combo box ----------------------------------------------------------
CLanguageComboBox(QWidget * parent)612 CLanguageComboBox::CLanguageComboBox ( QWidget* parent ) : QComboBox ( parent ), iIdxSelectedLanguage ( INVALID_INDEX )
613 {
614 QObject::connect ( this, static_cast<void ( QComboBox::* ) ( int )> ( &QComboBox::activated ), this, &CLanguageComboBox::OnLanguageActivated );
615 }
616
Init(QString & strSelLanguage)617 void CLanguageComboBox::Init ( QString& strSelLanguage )
618 {
619 // load available translations
620 const QMap<QString, QString> TranslMap = CLocale::GetAvailableTranslations();
621 QMapIterator<QString, QString> MapIter ( TranslMap );
622
623 // add translations to the combobox list
624 clear();
625 int iCnt = 0;
626 int iIdxOfEnglishLanguage = 0;
627 iIdxSelectedLanguage = INVALID_INDEX;
628
629 while ( MapIter.hasNext() )
630 {
631 MapIter.next();
632 addItem ( QLocale ( MapIter.key() ).nativeLanguageName() + " (" + MapIter.key() + ")", MapIter.key() );
633
634 // store the combo box index of the default english language
635 if ( MapIter.key().compare ( "en" ) == 0 )
636 {
637 iIdxOfEnglishLanguage = iCnt;
638 }
639
640 // if the selected language is found, store the combo box index
641 if ( MapIter.key().compare ( strSelLanguage ) == 0 )
642 {
643 iIdxSelectedLanguage = iCnt;
644 }
645
646 iCnt++;
647 }
648
649 // if the selected language was not found, use the english language
650 if ( iIdxSelectedLanguage == INVALID_INDEX )
651 {
652 strSelLanguage = "en";
653 iIdxSelectedLanguage = iIdxOfEnglishLanguage;
654 }
655
656 setCurrentIndex ( iIdxSelectedLanguage );
657 }
658
OnLanguageActivated(int iLanguageIdx)659 void CLanguageComboBox::OnLanguageActivated ( int iLanguageIdx )
660 {
661 // only update if the language selection is different from the current selected language
662 if ( iIdxSelectedLanguage != iLanguageIdx )
663 {
664 QMessageBox::information ( this, tr ( "Restart Required" ), tr ( "Please restart the application for the language change to take effect." ) );
665
666 emit LanguageChanged ( itemData ( iLanguageIdx ).toString() );
667 }
668 }
669 #endif
670
671 /******************************************************************************\
672 * Other Classes *
673 \******************************************************************************/
674 // Network utility functions ---------------------------------------------------
ParseNetworkAddress(QString strAddress,CHostAddress & HostAddress,bool bEnableIPv6)675 bool NetworkUtil::ParseNetworkAddress ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 )
676 {
677 QHostAddress InetAddr;
678 unsigned int iNetPort = DEFAULT_PORT_NUMBER;
679
680 // qInfo() << qUtf8Printable ( QString ( "Parsing network address %1" ).arg ( strAddress ) );
681
682 // init requested host address with invalid address first
683 HostAddress = CHostAddress();
684
685 // Allow the following address formats:
686 // [addr4or6]
687 // [addr4or6]:port
688 // addr4
689 // addr4:port
690 // hostname
691 // hostname:port
692 // (where addr4or6 is a literal IPv4 or IPv6 address, and addr4 is a literal IPv4 address
693
694 bool bLiteralAddr = false;
695 QRegExp rx1 ( "^\\[([^]]*)\\](?::(\\d+))?$" ); // [addr4or6] or [addr4or6]:port
696 QRegExp rx2 ( "^([^:]*)(?::(\\d+))?$" ); // addr4 or addr4:port or host or host:port
697
698 QString strPort;
699
700 // parse input address with rx1 and rx2 in turn, capturing address/host and port
701 if ( rx1.indexIn ( strAddress ) == 0 )
702 {
703 // literal address within []
704 strAddress = rx1.cap ( 1 );
705 strPort = rx1.cap ( 2 );
706 bLiteralAddr = true; // don't allow hostname within []
707 }
708 else if ( rx2.indexIn ( strAddress ) == 0 )
709 {
710 // hostname or IPv4 address
711 strAddress = rx2.cap ( 1 );
712 strPort = rx2.cap ( 2 );
713 }
714 else
715 {
716 // invalid format
717 // qInfo() << qUtf8Printable ( QString ( "Invalid address format" ) );
718 return false;
719 }
720
721 if ( !strPort.isEmpty() )
722 {
723 // a port number was given: extract port number
724 iNetPort = strPort.toInt();
725
726 if ( iNetPort >= 65536 )
727 {
728 // invalid port number
729 // qInfo() << qUtf8Printable ( QString ( "Invalid port number specified" ) );
730 return false;
731 }
732 }
733
734 // first try if this is an IP number an can directly applied to QHostAddress
735 if ( InetAddr.setAddress ( strAddress ) )
736 {
737 if ( !bEnableIPv6 && InetAddr.protocol() == QAbstractSocket::IPv6Protocol )
738 {
739 // do not allow IPv6 addresses if not enabled
740 // qInfo() << qUtf8Printable ( QString ( "IPv6 addresses disabled" ) );
741 return false;
742 }
743 }
744 else
745 {
746 // it was no valid IP address. If literal required, return as invalid
747 if ( bLiteralAddr )
748 {
749 // qInfo() << qUtf8Printable ( QString ( "Invalid literal IP address" ) );
750 return false; // invalid address
751 }
752
753 // try to get host by name, assuming
754 // that the string contains a valid host name string
755 const QHostInfo HostInfo = QHostInfo::fromName ( strAddress );
756
757 if ( HostInfo.error() != QHostInfo::NoError )
758 {
759 // qInfo() << qUtf8Printable ( QString ( "Invalid hostname" ) );
760 return false; // invalid address
761 }
762
763 bool bFoundAddr = false;
764
765 foreach ( const QHostAddress HostAddr, HostInfo.addresses() )
766 {
767 // qInfo() << qUtf8Printable ( QString ( "Resolved network address to %1 for proto %2" ) .arg ( HostAddr.toString() ) .arg (
768 // HostAddr.protocol() ) );
769 if ( HostAddr.protocol() == QAbstractSocket::IPv4Protocol || ( bEnableIPv6 && HostAddr.protocol() == QAbstractSocket::IPv6Protocol ) )
770 {
771 InetAddr = HostAddr;
772 bFoundAddr = true;
773 break;
774 }
775 }
776
777 if ( !bFoundAddr )
778 {
779 // no valid address found
780 // qInfo() << qUtf8Printable ( QString ( "No IP address found for hostname" ) );
781 return false;
782 }
783 }
784
785 // qInfo() << qUtf8Printable ( QString ( "Parsed network address %1" ).arg ( InetAddr.toString() ) );
786
787 HostAddress = CHostAddress ( InetAddr, iNetPort );
788
789 return true;
790 }
791
GetLocalAddress()792 CHostAddress NetworkUtil::GetLocalAddress()
793 {
794 QUdpSocket socket;
795 // As we are using UDP, the connectToHost() does not generate any traffic at all.
796 // We just require a socket which is pointed towards the Internet in
797 // order to find out the IP of our own external interface:
798 socket.connectToHost ( WELL_KNOWN_HOST, WELL_KNOWN_PORT );
799
800 if ( socket.waitForConnected ( IP_LOOKUP_TIMEOUT ) )
801 {
802 return CHostAddress ( socket.localAddress(), 0 );
803 }
804 else
805 {
806 qWarning() << "could not determine local IPv4 address:" << socket.errorString() << "- using localhost";
807
808 return CHostAddress ( QHostAddress::LocalHost, 0 );
809 }
810 }
811
GetLocalAddress6()812 CHostAddress NetworkUtil::GetLocalAddress6()
813 {
814 QUdpSocket socket;
815 // As we are using UDP, the connectToHost() does not generate any traffic at all.
816 // We just require a socket which is pointed towards the Internet in
817 // order to find out the IP of our own external interface:
818 socket.connectToHost ( WELL_KNOWN_HOST6, WELL_KNOWN_PORT );
819
820 if ( socket.waitForConnected ( IP_LOOKUP_TIMEOUT ) )
821 {
822 return CHostAddress ( socket.localAddress(), 0 );
823 }
824 else
825 {
826 qWarning() << "could not determine local IPv6 address:" << socket.errorString() << "- using localhost";
827
828 return CHostAddress ( QHostAddress::LocalHostIPv6, 0 );
829 }
830 }
831
GetCentralServerAddress(const ECSAddType eCentralServerAddressType,const QString & strCentralServerAddress)832 QString NetworkUtil::GetCentralServerAddress ( const ECSAddType eCentralServerAddressType, const QString& strCentralServerAddress )
833 {
834 switch ( eCentralServerAddressType )
835 {
836 case AT_CUSTOM:
837 return strCentralServerAddress;
838 case AT_ANY_GENRE2:
839 return CENTSERV_ANY_GENRE2;
840 case AT_ANY_GENRE3:
841 return CENTSERV_ANY_GENRE3;
842 case AT_GENRE_ROCK:
843 return CENTSERV_GENRE_ROCK;
844 case AT_GENRE_JAZZ:
845 return CENTSERV_GENRE_JAZZ;
846 case AT_GENRE_CLASSICAL_FOLK:
847 return CENTSERV_GENRE_CLASSICAL_FOLK;
848 case AT_GENRE_CHORAL:
849 return CENTSERV_GENRE_CHORAL;
850 default:
851 return DEFAULT_SERVER_ADDRESS; // AT_DEFAULT
852 }
853 }
854
FixAddress(const QString & strAddress)855 QString NetworkUtil::FixAddress ( const QString& strAddress )
856 {
857 // remove all spaces from the address string
858 return strAddress.simplified().replace ( " ", "" );
859 }
860
861 // Return whether the given HostAdress is within a private IP range
862 // as per RFC 1918 & RFC 5735.
IsPrivateNetworkIP(const QHostAddress & qhAddr)863 bool NetworkUtil::IsPrivateNetworkIP ( const QHostAddress& qhAddr )
864 {
865 // https://www.rfc-editor.org/rfc/rfc1918
866 // https://www.rfc-editor.org/rfc/rfc5735
867 static QList<QPair<QHostAddress, int>> addresses = {
868 QPair<QHostAddress, int> ( QHostAddress ( "10.0.0.0" ), 8 ),
869 QPair<QHostAddress, int> ( QHostAddress ( "127.0.0.0" ), 8 ),
870 QPair<QHostAddress, int> ( QHostAddress ( "172.16.0.0" ), 12 ),
871 QPair<QHostAddress, int> ( QHostAddress ( "192.168.0.0" ), 16 ),
872 };
873
874 foreach ( auto item, addresses )
875 {
876 if ( qhAddr.isInSubnet ( item ) )
877 {
878 return true;
879 }
880 }
881 return false;
882 }
883
884 // CHostAddress methods
885 // Compare() - compare two CHostAddress objects, and return an ordering between them:
886 // 0 - they are equal
887 // <0 - this comes before other
888 // >0 - this comes after other
889 // The order is not important, so long as it is consistent, for use in a binary search.
890
Compare(const CHostAddress & other) const891 int CHostAddress::Compare ( const CHostAddress& other ) const
892 {
893 // compare port first, as it is cheap, and clients will often use random ports
894
895 if ( iPort != other.iPort )
896 {
897 return (int) iPort - (int) other.iPort;
898 }
899
900 // compare protocols before addresses
901
902 QAbstractSocket::NetworkLayerProtocol thisProto = InetAddr.protocol();
903 QAbstractSocket::NetworkLayerProtocol otherProto = other.InetAddr.protocol();
904
905 if ( thisProto != otherProto )
906 {
907 return (int) thisProto - (int) otherProto;
908 }
909
910 // now we know both addresses are the same protocol
911
912 if ( thisProto == QAbstractSocket::IPv6Protocol )
913 {
914 // compare IPv6 addresses
915 Q_IPV6ADDR thisAddr = InetAddr.toIPv6Address();
916 Q_IPV6ADDR otherAddr = other.InetAddr.toIPv6Address();
917
918 return memcmp ( &thisAddr, &otherAddr, sizeof ( Q_IPV6ADDR ) );
919 }
920
921 // compare IPv4 addresses
922 quint32 thisAddr = InetAddr.toIPv4Address();
923 quint32 otherAddr = other.InetAddr.toIPv4Address();
924
925 return thisAddr < otherAddr ? -1 : thisAddr > otherAddr ? 1 : 0;
926 }
927
928 // Instrument picture data base ------------------------------------------------
GetTable(const bool bReGenerateTable)929 CVector<CInstPictures::CInstPictProps>& CInstPictures::GetTable ( const bool bReGenerateTable )
930 {
931 // make sure we generate the table only once
932 static bool TableIsInitialized = false;
933
934 static CVector<CInstPictProps> vecDataBase;
935
936 if ( !TableIsInitialized || bReGenerateTable )
937 {
938 // instrument picture data base initialization
939 // NOTE: Do not change the order of any instrument in the future!
940 // NOTE: The very first entry is the "not used" element per definition.
941 vecDataBase.Init ( 0 ); // first clear all existing data since we create the list be adding entries
942 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "None" ),
943 ":/png/instr/res/instruments/none.png",
944 IC_OTHER_INSTRUMENT ) ); // special first element
945 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Drum Set" ),
946 ":/png/instr/res/instruments/drumset.png",
947 IC_PERCUSSION_INSTRUMENT ) );
948 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Djembe" ),
949 ":/png/instr/res/instruments/djembe.png",
950 IC_PERCUSSION_INSTRUMENT ) );
951 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Electric Guitar" ),
952 ":/png/instr/res/instruments/eguitar.png",
953 IC_PLUCKING_INSTRUMENT ) );
954 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Acoustic Guitar" ),
955 ":/png/instr/res/instruments/aguitar.png",
956 IC_PLUCKING_INSTRUMENT ) );
957 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Bass Guitar" ),
958 ":/png/instr/res/instruments/bassguitar.png",
959 IC_PLUCKING_INSTRUMENT ) );
960 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Keyboard" ),
961 ":/png/instr/res/instruments/keyboard.png",
962 IC_KEYBOARD_INSTRUMENT ) );
963 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Synthesizer" ),
964 ":/png/instr/res/instruments/synthesizer.png",
965 IC_KEYBOARD_INSTRUMENT ) );
966 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Grand Piano" ),
967 ":/png/instr/res/instruments/grandpiano.png",
968 IC_KEYBOARD_INSTRUMENT ) );
969 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Accordion" ),
970 ":/png/instr/res/instruments/accordeon.png",
971 IC_KEYBOARD_INSTRUMENT ) );
972 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Vocal" ),
973 ":/png/instr/res/instruments/vocal.png",
974 IC_OTHER_INSTRUMENT ) );
975 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Microphone" ),
976 ":/png/instr/res/instruments/microphone.png",
977 IC_OTHER_INSTRUMENT ) );
978 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Harmonica" ),
979 ":/png/instr/res/instruments/harmonica.png",
980 IC_WIND_INSTRUMENT ) );
981 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Trumpet" ),
982 ":/png/instr/res/instruments/trumpet.png",
983 IC_WIND_INSTRUMENT ) );
984 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Trombone" ),
985 ":/png/instr/res/instruments/trombone.png",
986 IC_WIND_INSTRUMENT ) );
987 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "French Horn" ),
988 ":/png/instr/res/instruments/frenchhorn.png",
989 IC_WIND_INSTRUMENT ) );
990 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Tuba" ),
991 ":/png/instr/res/instruments/tuba.png",
992 IC_WIND_INSTRUMENT ) );
993 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Saxophone" ),
994 ":/png/instr/res/instruments/saxophone.png",
995 IC_WIND_INSTRUMENT ) );
996 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Clarinet" ),
997 ":/png/instr/res/instruments/clarinet.png",
998 IC_WIND_INSTRUMENT ) );
999 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Flute" ),
1000 ":/png/instr/res/instruments/flute.png",
1001 IC_WIND_INSTRUMENT ) );
1002 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Violin" ),
1003 ":/png/instr/res/instruments/violin.png",
1004 IC_STRING_INSTRUMENT ) );
1005 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Cello" ),
1006 ":/png/instr/res/instruments/cello.png",
1007 IC_STRING_INSTRUMENT ) );
1008 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Double Bass" ),
1009 ":/png/instr/res/instruments/doublebass.png",
1010 IC_STRING_INSTRUMENT ) );
1011 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Recorder" ),
1012 ":/png/instr/res/instruments/recorder.png",
1013 IC_OTHER_INSTRUMENT ) );
1014 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Streamer" ),
1015 ":/png/instr/res/instruments/streamer.png",
1016 IC_OTHER_INSTRUMENT ) );
1017 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Listener" ),
1018 ":/png/instr/res/instruments/listener.png",
1019 IC_OTHER_INSTRUMENT ) );
1020 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Guitar+Vocal" ),
1021 ":/png/instr/res/instruments/guitarvocal.png",
1022 IC_MULTIPLE_INSTRUMENT ) );
1023 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Keyboard+Vocal" ),
1024 ":/png/instr/res/instruments/keyboardvocal.png",
1025 IC_MULTIPLE_INSTRUMENT ) );
1026 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Bodhran" ),
1027 ":/png/instr/res/instruments/bodhran.png",
1028 IC_PERCUSSION_INSTRUMENT ) );
1029 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Bassoon" ),
1030 ":/png/instr/res/instruments/bassoon.png",
1031 IC_WIND_INSTRUMENT ) );
1032 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Oboe" ),
1033 ":/png/instr/res/instruments/oboe.png",
1034 IC_WIND_INSTRUMENT ) );
1035 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Harp" ),
1036 ":/png/instr/res/instruments/harp.png",
1037 IC_STRING_INSTRUMENT ) );
1038 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Viola" ),
1039 ":/png/instr/res/instruments/viola.png",
1040 IC_STRING_INSTRUMENT ) );
1041 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Congas" ),
1042 ":/png/instr/res/instruments/congas.png",
1043 IC_PERCUSSION_INSTRUMENT ) );
1044 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Bongo" ),
1045 ":/png/instr/res/instruments/bongo.png",
1046 IC_PERCUSSION_INSTRUMENT ) );
1047 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Vocal Bass" ),
1048 ":/png/instr/res/instruments/vocalbass.png",
1049 IC_OTHER_INSTRUMENT ) );
1050 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Vocal Tenor" ),
1051 ":/png/instr/res/instruments/vocaltenor.png",
1052 IC_OTHER_INSTRUMENT ) );
1053 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Vocal Alto" ),
1054 ":/png/instr/res/instruments/vocalalto.png",
1055 IC_OTHER_INSTRUMENT ) );
1056 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Vocal Soprano" ),
1057 ":/png/instr/res/instruments/vocalsoprano.png",
1058 IC_OTHER_INSTRUMENT ) );
1059 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Banjo" ),
1060 ":/png/instr/res/instruments/banjo.png",
1061 IC_PLUCKING_INSTRUMENT ) );
1062 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Mandolin" ),
1063 ":/png/instr/res/instruments/mandolin.png",
1064 IC_PLUCKING_INSTRUMENT ) );
1065 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Ukulele" ),
1066 ":/png/instr/res/instruments/ukulele.png",
1067 IC_PLUCKING_INSTRUMENT ) );
1068 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Bass Ukulele" ),
1069 ":/png/instr/res/instruments/bassukulele.png",
1070 IC_PLUCKING_INSTRUMENT ) );
1071 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Vocal Baritone" ),
1072 ":/png/instr/res/instruments/vocalbaritone.png",
1073 IC_OTHER_INSTRUMENT ) );
1074 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Vocal Lead" ),
1075 ":/png/instr/res/instruments/vocallead.png",
1076 IC_OTHER_INSTRUMENT ) );
1077 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Mountain Dulcimer" ),
1078 ":/png/instr/res/instruments/mountaindulcimer.png",
1079 IC_STRING_INSTRUMENT ) );
1080 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Scratching" ),
1081 ":/png/instr/res/instruments/scratching.png",
1082 IC_OTHER_INSTRUMENT ) );
1083 vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CClientSettingsDlg", "Rapping" ),
1084 ":/png/instr/res/instruments/rapping.png",
1085 IC_OTHER_INSTRUMENT ) );
1086
1087 // now the table is initialized
1088 TableIsInitialized = true;
1089 }
1090
1091 return vecDataBase;
1092 }
1093
IsInstIndexInRange(const int iIdx)1094 bool CInstPictures::IsInstIndexInRange ( const int iIdx )
1095 {
1096 // check if index is in valid range
1097 return ( iIdx >= 0 ) && ( iIdx < GetTable().Size() );
1098 }
1099
GetResourceReference(const int iInstrument)1100 QString CInstPictures::GetResourceReference ( const int iInstrument )
1101 {
1102 // range check
1103 if ( IsInstIndexInRange ( iInstrument ) )
1104 {
1105 // return the string of the resource reference for accessing the picture
1106 return GetTable()[iInstrument].strResourceReference;
1107 }
1108 else
1109 {
1110 return "";
1111 }
1112 }
1113
GetName(const int iInstrument)1114 QString CInstPictures::GetName ( const int iInstrument )
1115 {
1116 // range check
1117 if ( IsInstIndexInRange ( iInstrument ) )
1118 {
1119 // return the name of the instrument
1120 return GetTable()[iInstrument].strName;
1121 }
1122 else
1123 {
1124 return "";
1125 }
1126 }
1127
GetCategory(const int iInstrument)1128 CInstPictures::EInstCategory CInstPictures::GetCategory ( const int iInstrument )
1129 {
1130 // range check
1131 if ( IsInstIndexInRange ( iInstrument ) )
1132 {
1133 // return the name of the instrument
1134 return GetTable()[iInstrument].eInstCategory;
1135 }
1136 else
1137 {
1138 return IC_OTHER_INSTRUMENT;
1139 }
1140 }
1141
1142 // Locale management class -----------------------------------------------------
GetCountryFlagIconsResourceReference(const QLocale::Country eCountry)1143 QString CLocale::GetCountryFlagIconsResourceReference ( const QLocale::Country eCountry )
1144 {
1145 QString strReturn = "";
1146
1147 // special flag for none
1148 if ( eCountry == QLocale::AnyCountry )
1149 {
1150 strReturn = ":/png/flags/res/flags/flagnone.png";
1151 }
1152 else
1153 {
1154 // NOTE: The following code was introduced to support old QT versions. The problem
1155 // is that the number of countries displayed is less than the one displayed
1156 // with the new code below (which is disabled). Therefore, as soon as the
1157 // compatibility to the very old versions of QT is not required anymore, use
1158 // the new code.
1159 // COMPATIBLE FOR OLD QT VERSIONS -> use a table:
1160 QString strISO3166 = "";
1161 switch ( static_cast<int> ( eCountry ) )
1162 {
1163 case 1:
1164 strISO3166 = "af";
1165 break;
1166 case 2:
1167 strISO3166 = "al";
1168 break;
1169 case 3:
1170 strISO3166 = "dz";
1171 break;
1172 case 5:
1173 strISO3166 = "ad";
1174 break;
1175 case 6:
1176 strISO3166 = "ao";
1177 break;
1178 case 10:
1179 strISO3166 = "ar";
1180 break;
1181 case 11:
1182 strISO3166 = "am";
1183 break;
1184 case 12:
1185 strISO3166 = "aw";
1186 break;
1187 case 13:
1188 strISO3166 = "au";
1189 break;
1190 case 14:
1191 strISO3166 = "at";
1192 break;
1193 case 15:
1194 strISO3166 = "az";
1195 break;
1196 case 17:
1197 strISO3166 = "bh";
1198 break;
1199 case 18:
1200 strISO3166 = "bd";
1201 break;
1202 case 20:
1203 strISO3166 = "by";
1204 break;
1205 case 21:
1206 strISO3166 = "be";
1207 break;
1208 case 23:
1209 strISO3166 = "bj";
1210 break;
1211 case 25:
1212 strISO3166 = "bt";
1213 break;
1214 case 26:
1215 strISO3166 = "bo";
1216 break;
1217 case 27:
1218 strISO3166 = "ba";
1219 break;
1220 case 28:
1221 strISO3166 = "bw";
1222 break;
1223 case 30:
1224 strISO3166 = "br";
1225 break;
1226 case 32:
1227 strISO3166 = "bn";
1228 break;
1229 case 33:
1230 strISO3166 = "bg";
1231 break;
1232 case 34:
1233 strISO3166 = "bf";
1234 break;
1235 case 35:
1236 strISO3166 = "bi";
1237 break;
1238 case 36:
1239 strISO3166 = "kh";
1240 break;
1241 case 37:
1242 strISO3166 = "cm";
1243 break;
1244 case 38:
1245 strISO3166 = "ca";
1246 break;
1247 case 39:
1248 strISO3166 = "cv";
1249 break;
1250 case 41:
1251 strISO3166 = "cf";
1252 break;
1253 case 42:
1254 strISO3166 = "td";
1255 break;
1256 case 43:
1257 strISO3166 = "cl";
1258 break;
1259 case 44:
1260 strISO3166 = "cn";
1261 break;
1262 case 47:
1263 strISO3166 = "co";
1264 break;
1265 case 48:
1266 strISO3166 = "km";
1267 break;
1268 case 49:
1269 strISO3166 = "cd";
1270 break;
1271 case 50:
1272 strISO3166 = "cg";
1273 break;
1274 case 52:
1275 strISO3166 = "cr";
1276 break;
1277 case 53:
1278 strISO3166 = "ci";
1279 break;
1280 case 54:
1281 strISO3166 = "hr";
1282 break;
1283 case 55:
1284 strISO3166 = "cu";
1285 break;
1286 case 56:
1287 strISO3166 = "cy";
1288 break;
1289 case 57:
1290 strISO3166 = "cz";
1291 break;
1292 case 58:
1293 strISO3166 = "dk";
1294 break;
1295 case 59:
1296 strISO3166 = "dj";
1297 break;
1298 case 61:
1299 strISO3166 = "do";
1300 break;
1301 case 62:
1302 strISO3166 = "tl";
1303 break;
1304 case 63:
1305 strISO3166 = "ec";
1306 break;
1307 case 64:
1308 strISO3166 = "eg";
1309 break;
1310 case 65:
1311 strISO3166 = "sv";
1312 break;
1313 case 66:
1314 strISO3166 = "gq";
1315 break;
1316 case 67:
1317 strISO3166 = "er";
1318 break;
1319 case 68:
1320 strISO3166 = "ee";
1321 break;
1322 case 69:
1323 strISO3166 = "et";
1324 break;
1325 case 71:
1326 strISO3166 = "fo";
1327 break;
1328 case 73:
1329 strISO3166 = "fi";
1330 break;
1331 case 74:
1332 strISO3166 = "fr";
1333 break;
1334 case 76:
1335 strISO3166 = "gf";
1336 break;
1337 case 77:
1338 strISO3166 = "pf";
1339 break;
1340 case 79:
1341 strISO3166 = "ga";
1342 break;
1343 case 81:
1344 strISO3166 = "ge";
1345 break;
1346 case 82:
1347 strISO3166 = "de";
1348 break;
1349 case 83:
1350 strISO3166 = "gh";
1351 break;
1352 case 85:
1353 strISO3166 = "gr";
1354 break;
1355 case 86:
1356 strISO3166 = "gl";
1357 break;
1358 case 88:
1359 strISO3166 = "gp";
1360 break;
1361 case 90:
1362 strISO3166 = "gt";
1363 break;
1364 case 91:
1365 strISO3166 = "gn";
1366 break;
1367 case 92:
1368 strISO3166 = "gw";
1369 break;
1370 case 93:
1371 strISO3166 = "gy";
1372 break;
1373 case 96:
1374 strISO3166 = "hn";
1375 break;
1376 case 97:
1377 strISO3166 = "hk";
1378 break;
1379 case 98:
1380 strISO3166 = "hu";
1381 break;
1382 case 99:
1383 strISO3166 = "is";
1384 break;
1385 case 100:
1386 strISO3166 = "in";
1387 break;
1388 case 101:
1389 strISO3166 = "id";
1390 break;
1391 case 102:
1392 strISO3166 = "ir";
1393 break;
1394 case 103:
1395 strISO3166 = "iq";
1396 break;
1397 case 104:
1398 strISO3166 = "ie";
1399 break;
1400 case 105:
1401 strISO3166 = "il";
1402 break;
1403 case 106:
1404 strISO3166 = "it";
1405 break;
1406 case 108:
1407 strISO3166 = "jp";
1408 break;
1409 case 109:
1410 strISO3166 = "jo";
1411 break;
1412 case 110:
1413 strISO3166 = "kz";
1414 break;
1415 case 111:
1416 strISO3166 = "ke";
1417 break;
1418 case 113:
1419 strISO3166 = "kp";
1420 break;
1421 case 114:
1422 strISO3166 = "kr";
1423 break;
1424 case 115:
1425 strISO3166 = "kw";
1426 break;
1427 case 116:
1428 strISO3166 = "kg";
1429 break;
1430 case 117:
1431 strISO3166 = "la";
1432 break;
1433 case 118:
1434 strISO3166 = "lv";
1435 break;
1436 case 119:
1437 strISO3166 = "lb";
1438 break;
1439 case 120:
1440 strISO3166 = "ls";
1441 break;
1442 case 122:
1443 strISO3166 = "ly";
1444 break;
1445 case 123:
1446 strISO3166 = "li";
1447 break;
1448 case 124:
1449 strISO3166 = "lt";
1450 break;
1451 case 125:
1452 strISO3166 = "lu";
1453 break;
1454 case 126:
1455 strISO3166 = "mo";
1456 break;
1457 case 127:
1458 strISO3166 = "mk";
1459 break;
1460 case 128:
1461 strISO3166 = "mg";
1462 break;
1463 case 130:
1464 strISO3166 = "my";
1465 break;
1466 case 132:
1467 strISO3166 = "ml";
1468 break;
1469 case 133:
1470 strISO3166 = "mt";
1471 break;
1472 case 135:
1473 strISO3166 = "mq";
1474 break;
1475 case 136:
1476 strISO3166 = "mr";
1477 break;
1478 case 137:
1479 strISO3166 = "mu";
1480 break;
1481 case 138:
1482 strISO3166 = "yt";
1483 break;
1484 case 139:
1485 strISO3166 = "mx";
1486 break;
1487 case 141:
1488 strISO3166 = "md";
1489 break;
1490 case 142:
1491 strISO3166 = "mc";
1492 break;
1493 case 143:
1494 strISO3166 = "mn";
1495 break;
1496 case 145:
1497 strISO3166 = "ma";
1498 break;
1499 case 146:
1500 strISO3166 = "mz";
1501 break;
1502 case 147:
1503 strISO3166 = "mm";
1504 break;
1505 case 148:
1506 strISO3166 = "na";
1507 break;
1508 case 150:
1509 strISO3166 = "np";
1510 break;
1511 case 151:
1512 strISO3166 = "nl";
1513 break;
1514 case 153:
1515 strISO3166 = "nc";
1516 break;
1517 case 154:
1518 strISO3166 = "nz";
1519 break;
1520 case 155:
1521 strISO3166 = "ni";
1522 break;
1523 case 156:
1524 strISO3166 = "ne";
1525 break;
1526 case 157:
1527 strISO3166 = "ng";
1528 break;
1529 case 161:
1530 strISO3166 = "no";
1531 break;
1532 case 162:
1533 strISO3166 = "om";
1534 break;
1535 case 163:
1536 strISO3166 = "pk";
1537 break;
1538 case 165:
1539 strISO3166 = "ps";
1540 break;
1541 case 166:
1542 strISO3166 = "pa";
1543 break;
1544 case 167:
1545 strISO3166 = "pg";
1546 break;
1547 case 168:
1548 strISO3166 = "py";
1549 break;
1550 case 169:
1551 strISO3166 = "pe";
1552 break;
1553 case 170:
1554 strISO3166 = "ph";
1555 break;
1556 case 172:
1557 strISO3166 = "pl";
1558 break;
1559 case 173:
1560 strISO3166 = "pt";
1561 break;
1562 case 174:
1563 strISO3166 = "pr";
1564 break;
1565 case 175:
1566 strISO3166 = "qa";
1567 break;
1568 case 176:
1569 strISO3166 = "re";
1570 break;
1571 case 177:
1572 strISO3166 = "ro";
1573 break;
1574 case 178:
1575 strISO3166 = "ru";
1576 break;
1577 case 179:
1578 strISO3166 = "rw";
1579 break;
1580 case 184:
1581 strISO3166 = "sm";
1582 break;
1583 case 185:
1584 strISO3166 = "st";
1585 break;
1586 case 186:
1587 strISO3166 = "sa";
1588 break;
1589 case 187:
1590 strISO3166 = "sn";
1591 break;
1592 case 188:
1593 strISO3166 = "sc";
1594 break;
1595 case 189:
1596 strISO3166 = "sl";
1597 break;
1598 case 190:
1599 strISO3166 = "sg";
1600 break;
1601 case 191:
1602 strISO3166 = "sk";
1603 break;
1604 case 192:
1605 strISO3166 = "si";
1606 break;
1607 case 194:
1608 strISO3166 = "so";
1609 break;
1610 case 195:
1611 strISO3166 = "za";
1612 break;
1613 case 197:
1614 strISO3166 = "es";
1615 break;
1616 case 198:
1617 strISO3166 = "lk";
1618 break;
1619 case 201:
1620 strISO3166 = "sd";
1621 break;
1622 case 202:
1623 strISO3166 = "sr";
1624 break;
1625 case 204:
1626 strISO3166 = "sz";
1627 break;
1628 case 205:
1629 strISO3166 = "se";
1630 break;
1631 case 206:
1632 strISO3166 = "ch";
1633 break;
1634 case 207:
1635 strISO3166 = "sy";
1636 break;
1637 case 208:
1638 strISO3166 = "tw";
1639 break;
1640 case 209:
1641 strISO3166 = "tj";
1642 break;
1643 case 210:
1644 strISO3166 = "tz";
1645 break;
1646 case 211:
1647 strISO3166 = "th";
1648 break;
1649 case 212:
1650 strISO3166 = "tg";
1651 break;
1652 case 214:
1653 strISO3166 = "to";
1654 break;
1655 case 216:
1656 strISO3166 = "tn";
1657 break;
1658 case 217:
1659 strISO3166 = "tr";
1660 break;
1661 case 221:
1662 strISO3166 = "ug";
1663 break;
1664 case 222:
1665 strISO3166 = "ua";
1666 break;
1667 case 223:
1668 strISO3166 = "ae";
1669 break;
1670 case 224:
1671 strISO3166 = "gb";
1672 break;
1673 case 225:
1674 strISO3166 = "us";
1675 break;
1676 case 227:
1677 strISO3166 = "uy";
1678 break;
1679 case 228:
1680 strISO3166 = "uz";
1681 break;
1682 case 231:
1683 strISO3166 = "ve";
1684 break;
1685 case 232:
1686 strISO3166 = "vn";
1687 break;
1688 case 236:
1689 strISO3166 = "eh";
1690 break;
1691 case 237:
1692 strISO3166 = "ye";
1693 break;
1694 case 239:
1695 strISO3166 = "zm";
1696 break;
1697 case 240:
1698 strISO3166 = "zw";
1699 break;
1700 case 242:
1701 strISO3166 = "me";
1702 break;
1703 case 243:
1704 strISO3166 = "rs";
1705 break;
1706 case 248:
1707 strISO3166 = "ax";
1708 break;
1709 }
1710 strReturn = ":/png/flags/res/flags/" + strISO3166 + ".png";
1711
1712 // check if file actually exists, if not then invalidate reference
1713 if ( !QFile::exists ( strReturn ) )
1714 {
1715 strReturn = "";
1716 }
1717
1718 // AT LEAST QT 4.8 IS REQUIRED:
1719 /*
1720 // There is no direct query of the country code in Qt, therefore we use a
1721 // workaround: Get the matching locales properties and split the name of
1722 // that since the second part is the country code
1723 QList<QLocale> vCurLocaleList = QLocale::matchingLocales ( QLocale::AnyLanguage,
1724 QLocale::AnyScript,
1725 eCountry );
1726
1727 // check if the matching locales query was successful
1728 if ( vCurLocaleList.size() > 0 )
1729 {
1730 QStringList vstrLocParts = vCurLocaleList.at ( 0 ).name().split("_");
1731
1732 // the second split contains the name we need
1733 if ( vstrLocParts.size() > 1 )
1734 {
1735 strReturn = ":/png/flags/res/flags/" + vstrLocParts.at ( 1 ).toLower() + ".png";
1736
1737 // check if file actually exists, if not then invalidate reference
1738 if ( !QFile::exists ( strReturn ) )
1739 {
1740 strReturn = "";
1741 }
1742 //else
1743 //{
1744 //// TEST generate table
1745 //static FILE* pFile = fopen ( "test.dat", "w" );
1746 //fprintf ( pFile, " case %d: strISO3166 = \"%s\"; break;\n",
1747 // static_cast<int> ( eCountry ), vstrLocParts.at ( 1 ).toLower().toStdString().c_str() );
1748 //fflush ( pFile );
1749 //}
1750 }
1751 }
1752 */
1753 }
1754
1755 return strReturn;
1756 }
1757
GetAvailableTranslations()1758 QMap<QString, QString> CLocale::GetAvailableTranslations()
1759 {
1760 QMap<QString, QString> TranslMap;
1761 QDirIterator DirIter ( ":/translations" );
1762
1763 // add english language (default which is in the actual source code)
1764 TranslMap["en"] = ""; // empty file name means that the translation load fails and we get the default english language
1765
1766 while ( DirIter.hasNext() )
1767 {
1768 // get alias of translation file
1769 const QString strCurFileName = DirIter.next();
1770
1771 // extract only language code (must be at the end, separated with a "_")
1772 const QString strLoc = strCurFileName.right ( strCurFileName.length() - strCurFileName.indexOf ( "_" ) - 1 );
1773
1774 TranslMap[strLoc] = strCurFileName;
1775 }
1776
1777 return TranslMap;
1778 }
1779
FindSysLangTransFileName(const QMap<QString,QString> & TranslMap)1780 QPair<QString, QString> CLocale::FindSysLangTransFileName ( const QMap<QString, QString>& TranslMap )
1781 {
1782 QPair<QString, QString> PairSysLang ( "", "" );
1783 QStringList slUiLang = QLocale().uiLanguages();
1784
1785 if ( !slUiLang.isEmpty() )
1786 {
1787 QString strUiLang = QLocale().uiLanguages().at ( 0 );
1788 strUiLang.replace ( "-", "_" );
1789
1790 // first try to find the complete language string
1791 if ( TranslMap.constFind ( strUiLang ) != TranslMap.constEnd() )
1792 {
1793 PairSysLang.first = strUiLang;
1794 PairSysLang.second = TranslMap[PairSysLang.first];
1795 }
1796 else
1797 {
1798 // only extract two first characters to identify language (ignoring
1799 // location for getting a simpler implementation -> if the language
1800 // is not correct, the user can change it in the GUI anyway)
1801 if ( strUiLang.length() >= 2 )
1802 {
1803 PairSysLang.first = strUiLang.left ( 2 );
1804 PairSysLang.second = TranslMap[PairSysLang.first];
1805 }
1806 }
1807 }
1808
1809 return PairSysLang;
1810 }
1811
LoadTranslation(const QString strLanguage,QCoreApplication * pApp)1812 void CLocale::LoadTranslation ( const QString strLanguage, QCoreApplication* pApp )
1813 {
1814 // The translator objects must be static!
1815 static QTranslator myappTranslator;
1816 static QTranslator myqtTranslator;
1817
1818 QMap<QString, QString> TranslMap = CLocale::GetAvailableTranslations();
1819 const QString strTranslationFileName = TranslMap[strLanguage];
1820
1821 if ( myappTranslator.load ( strTranslationFileName ) )
1822 {
1823 pApp->installTranslator ( &myappTranslator );
1824 }
1825
1826 // allows the Qt messages to be translated in the application
1827 if ( myqtTranslator.load ( QLocale ( strLanguage ), "qt", "_", QLibraryInfo::location ( QLibraryInfo::TranslationsPath ) ) )
1828 {
1829 pApp->installTranslator ( &myqtTranslator );
1830 }
1831 }
1832
1833 /******************************************************************************\
1834 * Global Functions Implementation *
1835 \******************************************************************************/
GetVersionAndNameStr(const bool bWithHtml)1836 QString GetVersionAndNameStr ( const bool bWithHtml )
1837 {
1838 QString strVersionText = "";
1839
1840 // name, short description and GPL hint
1841 if ( bWithHtml )
1842 {
1843 strVersionText += "<b>";
1844 }
1845 else
1846 {
1847 strVersionText += " *** ";
1848 }
1849
1850 strVersionText += APP_NAME + QCoreApplication::tr ( ", Version " ) + VERSION;
1851
1852 if ( bWithHtml )
1853 {
1854 strVersionText += "</b><br>";
1855 }
1856 else
1857 {
1858 strVersionText += "\n *** ";
1859 }
1860
1861 if ( !bWithHtml )
1862 {
1863 strVersionText += QCoreApplication::tr ( "Internet Jam Session Software" );
1864 strVersionText += "\n *** ";
1865 }
1866
1867 strVersionText += QCoreApplication::tr ( "Released under the GNU General Public License (GPL)" );
1868
1869 return strVersionText;
1870 }
1871
MakeClientNameTitle(QString win,QString client)1872 QString MakeClientNameTitle ( QString win, QString client )
1873 {
1874 QString sReturnString = win;
1875 if ( !client.isEmpty() )
1876 {
1877 sReturnString += " - " + client;
1878 }
1879 return ( sReturnString );
1880 }
1881