OSDN Git Service

Reorganized global functions: The file "Global.h" was split into multiple file in...
[lamexp/LameXP.git] / src / Global_Utils.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2013 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version, but always including the *additional*
9 // restrictions defined in the "License.txt" file.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 //
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
22
23 #include "Global.h"
24
25 //Qt includes
26 #include <QApplication>
27 #include <QDate>
28 #include <QDir>
29 #include <QFileInfo>
30 #include <QIcon>
31 #include <QMutex>
32 #include <QProcessEnvironment>
33 #include <QReadWriteLock>
34 #include <QTextCodec>
35 #include <QUuid>
36 #include <QWidget>
37
38 //Natural sort
39 #include "strnatcmp.h"
40
41 //CRT includes
42 #include <time.h>
43 #include <process.h>
44
45 ///////////////////////////////////////////////////////////////////////////////
46 // GLOBAL VARS
47 ///////////////////////////////////////////////////////////////////////////////
48
49 //%TEMP% folder
50 static struct
51 {
52         QString *path;
53         QReadWriteLock lock;
54 }
55 g_lamexp_temp_folder;
56
57 ///////////////////////////////////////////////////////////////////////////////
58 // GLOBAL FUNCTIONS
59 ///////////////////////////////////////////////////////////////////////////////
60
61 /*
62  * Get a random string
63  */
64 QString lamexp_rand_str(const bool bLong)
65 {
66         const QUuid uuid = QUuid::createUuid().toString();
67
68         const unsigned int u1 = uuid.data1;
69         const unsigned int u2 = (((unsigned int)(uuid.data2)) << 16) | ((unsigned int)(uuid.data3));
70         const unsigned int u3 = (((unsigned int)(uuid.data4[0])) << 24) | (((unsigned int)(uuid.data4[1])) << 16) | (((unsigned int)(uuid.data4[2])) << 8) | ((unsigned int)(uuid.data4[3]));
71         const unsigned int u4 = (((unsigned int)(uuid.data4[4])) << 24) | (((unsigned int)(uuid.data4[5])) << 16) | (((unsigned int)(uuid.data4[6])) << 8) | ((unsigned int)(uuid.data4[7]));
72
73         return bLong ? QString().sprintf("%08x%08x%08x%08x", u1, u2, u3, u4) : QString().sprintf("%08x%08x", (u1 ^ u2), (u3 ^ u4));
74 }
75
76 /*
77  * Try to initialize the folder (with *write* access)
78  */
79 static QString lamexp_try_init_folder(const QString &folderPath)
80 {
81         bool success = false;
82
83         const QFileInfo folderInfo(folderPath);
84         const QDir folderDir(folderInfo.absoluteFilePath());
85
86         //Create folder, if it does *not* exist yet
87         if(!folderDir.exists())
88         {
89                 folderDir.mkpath(".");
90         }
91
92         //Make sure folder exists now *and* is writable
93         if(folderDir.exists())
94         {
95                 QFile testFile(folderDir.absoluteFilePath(QString("~%1.tmp").arg(lamexp_rand_str())));
96                 if(testFile.open(QIODevice::ReadWrite))
97                 {
98                         const QByteArray testData = QByteArray("Lorem ipsum dolor sit amet, consectetur, adipisci velit!");
99                         if(testFile.write(testData) >= strlen(testData))
100                         {
101                                 success = true;
102                                 testFile.remove();
103                         }
104                         testFile.close();
105                 }
106         }
107
108         return (success ? folderDir.canonicalPath() : QString());
109 }
110
111 /*
112  * Initialize LameXP temp folder
113  */
114 #define INIT_TEMP_FOLDER(OUT,TMP) do \
115 { \
116         (OUT) = lamexp_try_init_folder(QString("%1/%2").arg((TMP), lamexp_rand_str())); \
117 } \
118 while(0)
119
120 /*
121  * Get LameXP temp folder
122  */
123 const QString &lamexp_temp_folder2(void)
124 {
125         QReadLocker readLock(&g_lamexp_temp_folder.lock);
126
127         //Already initialized?
128         if(g_lamexp_temp_folder.path && (!g_lamexp_temp_folder.path->isEmpty()))
129         {
130                 if(QDir(*g_lamexp_temp_folder.path).exists())
131                 {
132                         return *g_lamexp_temp_folder.path;
133                 }
134         }
135
136         //Obtain the write lock to initilaize
137         readLock.unlock();
138         QWriteLocker writeLock(&g_lamexp_temp_folder.lock);
139         
140         //Still uninitilaized?
141         if(g_lamexp_temp_folder.path && (!g_lamexp_temp_folder.path->isEmpty()))
142         {
143                 if(QDir(*g_lamexp_temp_folder.path).exists())
144                 {
145                         return *g_lamexp_temp_folder.path;
146                 }
147         }
148
149         //Create the string, if not done yet
150         if(!g_lamexp_temp_folder.path)
151         {
152                 g_lamexp_temp_folder.path = new QString();
153         }
154         
155         g_lamexp_temp_folder.path->clear();
156
157         //Try the %TMP% or %TEMP% directory first
158         QString tempPath = lamexp_try_init_folder(QDir::temp().absolutePath());
159         if(!tempPath.isEmpty())
160         {
161                 INIT_TEMP_FOLDER(*g_lamexp_temp_folder.path, tempPath);
162         }
163
164         //Otherwise create TEMP folder in %LOCALAPPDATA%
165         if(g_lamexp_temp_folder.path->isEmpty())
166         {
167                 tempPath = lamexp_try_init_folder(QString("%1/Temp").arg(lamexp_known_folder(lamexp_folder_localappdata)));
168                 if(!tempPath.isEmpty())
169                 {
170                         INIT_TEMP_FOLDER(*g_lamexp_temp_folder.path, tempPath);
171                 }
172         }
173
174         //Failed to create TEMP folder?
175         if(g_lamexp_temp_folder.path->isEmpty())
176         {
177                 qFatal("Temporary directory could not be initialized !!!");
178         }
179         
180         return *g_lamexp_temp_folder.path;
181 }
182
183 /*
184  * Clear LameXP temp folder cache
185  */
186 void lamexp_temp_folder_clear(void)
187 {
188         QWriteLocker writeLock(&g_lamexp_temp_folder.lock);
189         LAMEXP_DELETE(g_lamexp_temp_folder.path);
190 }
191
192 /*
193  * Setup QPorcess object
194  */
195 void lamexp_init_process(QProcess &process, const QString &wokringDir)
196 {
197         //Environment variable names
198         static const char *const s_envvar_names[] =
199         {
200                 "WGETRC", "SYSTEM_WGETRC", "HTTP_PROXY", "FTP_PROXY", "NO_PROXY", "GNUPGHOME", "LC_ALL", "LC_COLLATE", "LC_CTYPE", "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME", "LANG", NULL
201         };
202
203         const QString tempDir = QDir::toNativeSeparators(lamexp_temp_folder2());
204
205         QProcessEnvironment env = process.processEnvironment();
206         if(env.isEmpty()) env = QProcessEnvironment::systemEnvironment();
207
208         //Setup TEMP directory
209         env.insert("TEMP", tempDir);
210         env.insert("TMP", tempDir);
211         env.insert("TMPDIR", tempDir);
212         env.insert("HOME", tempDir);
213         env.insert("USERPROFILE", tempDir);
214         env.insert("HOMEPATH", tempDir);
215
216         //Setup PATH variable
217         const QString path = env.value("PATH", QString());
218         env.insert("PATH", path.isEmpty() ? tempDir : QString("%1;%2").arg(tempDir, path));
219
220         //Clean a number of enviroment variables that might affect our tools
221         for(size_t i = 0; s_envvar_names[i]; i++)
222         {
223                 env.remove(QString::fromLatin1(s_envvar_names[i]));
224                 env.remove(QString::fromLatin1(s_envvar_names[i]).toLower());
225         }
226         
227         process.setWorkingDirectory(wokringDir);
228         process.setProcessChannelMode(QProcess::MergedChannels);
229         process.setReadChannel(QProcess::StandardOutput);
230         process.setProcessEnvironment(env);
231 }
232
233 /*
234  * Natural Order String Comparison - the 'lessThan' helper function
235  */
236 static bool lamexp_natural_string_sort_helper(const QString &str1, const QString &str2)
237 {
238         return (strnatcmp(QWCHAR(str1), QWCHAR(str2)) < 0);
239 }
240
241 /*
242  * Natural Order String Comparison - the 'lessThan' helper function *with* case folding
243  */
244 static bool lamexp_natural_string_sort_helper_fold_case(const QString &str1, const QString &str2)
245 {
246         return (strnatcasecmp(QWCHAR(str1), QWCHAR(str2)) < 0);
247 }
248
249 /*
250  * Natural Order String Comparison - the main sorting function
251  */
252 void lamexp_natural_string_sort(QStringList &list, const bool bIgnoreCase)
253 {
254         qSort(list.begin(), list.end(), bIgnoreCase ? lamexp_natural_string_sort_helper_fold_case : lamexp_natural_string_sort_helper);
255 }
256
257 /*
258  * Remove forbidden characters from a filename
259  */
260 const QString lamexp_clean_filename(const QString &str)
261 {
262         QString newStr(str);
263         QRegExp rx("\"(.+)\"");
264         rx.setMinimal(true);
265
266         newStr.replace("\\", "-");
267         newStr.replace(" / ", ", ");
268         newStr.replace("/", ",");
269         newStr.replace(":", "-");
270         newStr.replace("*", "x");
271         newStr.replace("?", "");
272         newStr.replace("<", "[");
273         newStr.replace(">", "]");
274         newStr.replace("|", "!");
275         newStr.replace(rx, "`\\1´");
276         newStr.replace("\"", "'");
277         
278         return newStr.simplified();
279 }
280
281 /*
282  * Remove forbidden characters from a file path
283  */
284 const QString lamexp_clean_filepath(const QString &str)
285 {
286         QStringList parts = QString(str).replace("\\", "/").split("/");
287
288         for(int i = 0; i < parts.count(); i++)
289         {
290                 parts[i] = lamexp_clean_filename(parts[i]);
291         }
292
293         return parts.join("/");
294 }
295
296 /*
297  * Get a list of all available Qt Text Codecs
298  */
299 QStringList lamexp_available_codepages(bool noAliases)
300 {
301         QStringList codecList;
302         
303         QList<QByteArray> availableCodecs = QTextCodec::availableCodecs();
304         while(!availableCodecs.isEmpty())
305         {
306                 QByteArray current = availableCodecs.takeFirst();
307                 if(!(current.startsWith("system") || current.startsWith("System")))
308                 {
309                         codecList << QString::fromLatin1(current.constData(), current.size());
310                         if(noAliases)
311                         {
312                                 if(QTextCodec *currentCodec = QTextCodec::codecForName(current.constData()))
313                                 {
314                                         
315                                         QList<QByteArray> aliases = currentCodec->aliases();
316                                         while(!aliases.isEmpty()) availableCodecs.removeAll(aliases.takeFirst());
317                                 }
318                         }
319                 }
320         }
321
322         return codecList;
323 }
324
325 /*
326  * Robert Jenkins' 96 bit Mix Function
327  * Source: http://www.concentric.net/~Ttwang/tech/inthash.htm
328  */
329 static unsigned int lamexp_mix(const unsigned int x, const unsigned int y, const unsigned int z)
330 {
331         unsigned int a = x;
332         unsigned int b = y;
333         unsigned int c = z;
334         
335         a=a-b;  a=a-c;  a=a^(c >> 13);
336         b=b-c;  b=b-a;  b=b^(a << 8); 
337         c=c-a;  c=c-b;  c=c^(b >> 13);
338         a=a-b;  a=a-c;  a=a^(c >> 12);
339         b=b-c;  b=b-a;  b=b^(a << 16);
340         c=c-a;  c=c-b;  c=c^(b >> 5);
341         a=a-b;  a=a-c;  a=a^(c >> 3);
342         b=b-c;  b=b-a;  b=b^(a << 10);
343         c=c-a;  c=c-b;  c=c^(b >> 15);
344
345         return c;
346 }
347
348 /*
349  * Seeds the random number generator
350  * Note: Altough rand_s() doesn't need a seed, this must be called pripr to lamexp_rand(), just to to be sure!
351  */
352 void lamexp_seed_rand(void)
353 {
354         qsrand(lamexp_mix(clock(), time(NULL), _getpid()));
355 }
356
357 /*
358  * Returns a randum number
359  * Note: This function uses rand_s() if available, but falls back to qrand() otherwise
360  */
361 unsigned int lamexp_rand(void)
362 {
363         quint32 rnd = 0;
364
365         if(rand_s(&rnd) == 0)
366         {
367                 return rnd;
368         }
369
370         for(size_t i = 0; i < sizeof(unsigned int); i++)
371         {
372                 rnd = (rnd << 8) ^ qrand();
373         }
374
375         return rnd;
376 }
377
378 /*
379  * Make a window blink (to draw user's attention)
380  */
381 void lamexp_blink_window(QWidget *poWindow, unsigned int count, unsigned int delay)
382 {
383         static QMutex blinkMutex;
384
385         const double maxOpac = 1.0;
386         const double minOpac = 0.3;
387         const double delOpac = 0.1;
388
389         if(!blinkMutex.tryLock())
390         {
391                 qWarning("Blinking is already in progress, skipping!");
392                 return;
393         }
394         
395         try
396         {
397                 const int steps = static_cast<int>(ceil(maxOpac - minOpac) / delOpac);
398                 const int sleep = static_cast<int>(floor(static_cast<double>(delay) / static_cast<double>(steps)));
399                 const double opacity = poWindow->windowOpacity();
400         
401                 for(unsigned int i = 0; i < count; i++)
402                 {
403                         for(double x = maxOpac; x >= minOpac; x -= delOpac)
404                         {
405                                 poWindow->setWindowOpacity(x);
406                                 QApplication::processEvents();
407                                 lamexp_sleep(sleep);
408                         }
409
410                         for(double x = minOpac; x <= maxOpac; x += delOpac)
411                         {
412                                 poWindow->setWindowOpacity(x);
413                                 QApplication::processEvents();
414                                 lamexp_sleep(sleep);
415                         }
416                 }
417
418                 poWindow->setWindowOpacity(opacity);
419                 QApplication::processEvents();
420                 blinkMutex.unlock();
421         }
422         catch(...)
423         {
424                 blinkMutex.unlock();
425                 qWarning("Exception error while blinking!");
426         }
427 }
428
429 /*
430  * Clean folder
431  */
432 bool lamexp_clean_folder(const QString &folderPath)
433 {
434         QDir tempFolder(folderPath);
435         if(tempFolder.exists())
436         {
437                 QFileInfoList entryList = tempFolder.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot);
438
439                 for(int i = 0; i < entryList.count(); i++)
440                 {
441                         if(entryList.at(i).isDir())
442                         {
443                                 lamexp_clean_folder(entryList.at(i).canonicalFilePath());
444                         }
445                         else
446                         {
447                                 for(int j = 0; j < 3; j++)
448                                 {
449                                         if(lamexp_remove_file(entryList.at(i).canonicalFilePath()))
450                                         {
451                                                 break;
452                                         }
453                                 }
454                         }
455                 }
456                 return tempFolder.rmdir(".");
457         }
458         return true;
459 }
460
461 /*
462  * Computus according to H. Lichtenberg
463  */
464 static bool lamexp_computus(const QDate &date)
465 {
466         int X = date.year();
467         int A = X % 19;
468         int K = X / 100;
469         int M = 15 + (3*K + 3) / 4 - (8*K + 13) / 25;
470         int D = (19*A + M) % 30;
471         int S = 2 - (3*K + 3) / 4;
472         int R = D / 29 + (D / 28 - D / 29) * (A / 11);
473         int OG = 21 + D - R;
474         int SZ = 7 - (X + X / 4 + S) % 7;
475         int OE = 7 - (OG - SZ) % 7;
476         int OS = (OG + OE);
477
478         if(OS > 31)
479         {
480                 return (date.month() == 4) && (date.day() == (OS - 31));
481         }
482         else
483         {
484                 return (date.month() == 3) && (date.day() == OS);
485         }
486 }
487
488 /*
489  * Check for Thanksgiving
490  */
491 static bool lamexp_thanksgiving(const QDate &date)
492 {
493         int day = 0;
494
495         switch(QDate(date.year(), 11, 1).dayOfWeek())
496         {
497                 case 1: day = 25; break; 
498                 case 2: day = 24; break; 
499                 case 3: day = 23; break; 
500                 case 4: day = 22; break; 
501                 case 5: day = 28; break; 
502                 case 6: day = 27; break; 
503                 case 7: day = 26; break;
504         }
505
506         return (date.month() == 11) && (date.day() == day);
507 }
508
509 /*
510  * Initialize app icon
511  */
512 QIcon lamexp_app_icon(const QDate *date, const QTime *time)
513 {
514         QDate currentDate = (date) ? QDate(*date) : QDate::currentDate();
515         QTime currentTime = (time) ? QTime(*time) : QTime::currentTime();
516         
517         if(lamexp_thanksgiving(currentDate))
518         {
519                 return QIcon(":/MainIcon6.png");
520         }
521         else if(((currentDate.month() == 12) && (currentDate.day() == 31) && (currentTime.hour() >= 20)) || ((currentDate.month() == 1) && (currentDate.day() == 1)  && (currentTime.hour() <= 19)))
522         {
523                 return QIcon(":/MainIcon5.png");
524         }
525         else if(((currentDate.month() == 10) && (currentDate.day() == 31) && (currentTime.hour() >= 12)) || ((currentDate.month() == 11) && (currentDate.day() == 1)  && (currentTime.hour() <= 11)))
526         {
527                 return QIcon(":/MainIcon4.png");
528         }
529         else if((currentDate.month() == 12) && (currentDate.day() >= 24) && (currentDate.day() <= 26))
530         {
531                 return QIcon(":/MainIcon3.png");
532         }
533         else if(lamexp_computus(currentDate))
534         {
535                 return QIcon(":/MainIcon2.png");
536         }
537         else
538         {
539                 return QIcon(":/MainIcon1.png");
540         }
541 }
542
543 /*
544  * Broadcast event to all windows
545  */
546 bool lamexp_broadcast(int eventType, bool onlyToVisible)
547 {
548         if(QApplication *app = dynamic_cast<QApplication*>(QApplication::instance()))
549         {
550                 qDebug("Broadcasting %d", eventType);
551                 
552                 bool allOk = true;
553                 QEvent poEvent(static_cast<QEvent::Type>(eventType));
554                 QWidgetList list = app->topLevelWidgets();
555
556                 while(!list.isEmpty())
557                 {
558                         QWidget *widget = list.takeFirst();
559                         if(!onlyToVisible || widget->isVisible())
560                         {
561                                 if(!app->sendEvent(widget, &poEvent))
562                                 {
563                                         allOk = false;
564                                 }
565                         }
566                 }
567
568                 qDebug("Broadcast %d done (%s)", eventType, (allOk ? "OK" : "Stopped"));
569                 return allOk;
570         }
571         else
572         {
573                 qWarning("Broadcast failed, could not get QApplication instance!");
574                 return false;
575         }
576 }
577
578 ///////////////////////////////////////////////////////////////////////////////
579 // INITIALIZATION
580 ///////////////////////////////////////////////////////////////////////////////
581
582 extern "C" void _lamexp_global_init_utils(void)
583 {
584         LAMEXP_ZERO_MEMORY(g_lamexp_temp_folder);
585 }