1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2013 LoRd_MuldeR <MuldeR2@GMX.de>
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.
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.
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.
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
26 #include <QApplication>
32 #include <QProcessEnvironment>
33 #include <QReadWriteLock>
39 #include "strnatcmp.h"
45 ///////////////////////////////////////////////////////////////////////////////
47 ///////////////////////////////////////////////////////////////////////////////
57 ///////////////////////////////////////////////////////////////////////////////
59 ///////////////////////////////////////////////////////////////////////////////
64 QString lamexp_rand_str(const bool bLong)
66 const QUuid uuid = QUuid::createUuid().toString();
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]));
73 return bLong ? QString().sprintf("%08x%08x%08x%08x", u1, u2, u3, u4) : QString().sprintf("%08x%08x", (u1 ^ u2), (u3 ^ u4));
77 * Try to initialize the folder (with *write* access)
79 static QString lamexp_try_init_folder(const QString &folderPath)
83 const QFileInfo folderInfo(folderPath);
84 const QDir folderDir(folderInfo.absoluteFilePath());
86 //Create folder, if it does *not* exist yet
87 if(!folderDir.exists())
89 folderDir.mkpath(".");
92 //Make sure folder exists now *and* is writable
93 if(folderDir.exists())
95 QFile testFile(folderDir.absoluteFilePath(QString("~%1.tmp").arg(lamexp_rand_str())));
96 if(testFile.open(QIODevice::ReadWrite))
98 const QByteArray testData = QByteArray("Lorem ipsum dolor sit amet, consectetur, adipisci velit!");
99 if(testFile.write(testData) >= strlen(testData))
108 return (success ? folderDir.canonicalPath() : QString());
112 * Initialize LameXP temp folder
114 #define INIT_TEMP_FOLDER(OUT,TMP) do \
116 (OUT) = lamexp_try_init_folder(QString("%1/%2").arg((TMP), lamexp_rand_str())); \
121 * Get LameXP temp folder
123 const QString &lamexp_temp_folder2(void)
125 QReadLocker readLock(&g_lamexp_temp_folder.lock);
127 //Already initialized?
128 if(g_lamexp_temp_folder.path && (!g_lamexp_temp_folder.path->isEmpty()))
130 if(QDir(*g_lamexp_temp_folder.path).exists())
132 return *g_lamexp_temp_folder.path;
136 //Obtain the write lock to initilaize
138 QWriteLocker writeLock(&g_lamexp_temp_folder.lock);
140 //Still uninitilaized?
141 if(g_lamexp_temp_folder.path && (!g_lamexp_temp_folder.path->isEmpty()))
143 if(QDir(*g_lamexp_temp_folder.path).exists())
145 return *g_lamexp_temp_folder.path;
149 //Create the string, if not done yet
150 if(!g_lamexp_temp_folder.path)
152 g_lamexp_temp_folder.path = new QString();
155 g_lamexp_temp_folder.path->clear();
157 //Try the %TMP% or %TEMP% directory first
158 QString tempPath = lamexp_try_init_folder(QDir::temp().absolutePath());
159 if(!tempPath.isEmpty())
161 INIT_TEMP_FOLDER(*g_lamexp_temp_folder.path, tempPath);
164 //Otherwise create TEMP folder in %LOCALAPPDATA%
165 if(g_lamexp_temp_folder.path->isEmpty())
167 tempPath = lamexp_try_init_folder(QString("%1/Temp").arg(lamexp_known_folder(lamexp_folder_localappdata)));
168 if(!tempPath.isEmpty())
170 INIT_TEMP_FOLDER(*g_lamexp_temp_folder.path, tempPath);
174 //Failed to create TEMP folder?
175 if(g_lamexp_temp_folder.path->isEmpty())
177 qFatal("Temporary directory could not be initialized !!!");
180 return *g_lamexp_temp_folder.path;
184 * Clear LameXP temp folder cache
186 void lamexp_temp_folder_clear(void)
188 QWriteLocker writeLock(&g_lamexp_temp_folder.lock);
189 LAMEXP_DELETE(g_lamexp_temp_folder.path);
193 * Setup QPorcess object
195 void lamexp_init_process(QProcess &process, const QString &wokringDir)
197 //Environment variable names
198 static const char *const s_envvar_names[] =
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
203 const QString tempDir = QDir::toNativeSeparators(lamexp_temp_folder2());
205 QProcessEnvironment env = process.processEnvironment();
206 if(env.isEmpty()) env = QProcessEnvironment::systemEnvironment();
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);
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));
220 //Clean a number of enviroment variables that might affect our tools
221 for(size_t i = 0; s_envvar_names[i]; i++)
223 env.remove(QString::fromLatin1(s_envvar_names[i]));
224 env.remove(QString::fromLatin1(s_envvar_names[i]).toLower());
227 process.setWorkingDirectory(wokringDir);
228 process.setProcessChannelMode(QProcess::MergedChannels);
229 process.setReadChannel(QProcess::StandardOutput);
230 process.setProcessEnvironment(env);
234 * Natural Order String Comparison - the 'lessThan' helper function
236 static bool lamexp_natural_string_sort_helper(const QString &str1, const QString &str2)
238 return (strnatcmp(QWCHAR(str1), QWCHAR(str2)) < 0);
242 * Natural Order String Comparison - the 'lessThan' helper function *with* case folding
244 static bool lamexp_natural_string_sort_helper_fold_case(const QString &str1, const QString &str2)
246 return (strnatcasecmp(QWCHAR(str1), QWCHAR(str2)) < 0);
250 * Natural Order String Comparison - the main sorting function
252 void lamexp_natural_string_sort(QStringList &list, const bool bIgnoreCase)
254 qSort(list.begin(), list.end(), bIgnoreCase ? lamexp_natural_string_sort_helper_fold_case : lamexp_natural_string_sort_helper);
258 * Remove forbidden characters from a filename
260 const QString lamexp_clean_filename(const QString &str)
263 QRegExp rx("\"(.+)\"");
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("\"", "'");
278 return newStr.simplified();
282 * Remove forbidden characters from a file path
284 const QString lamexp_clean_filepath(const QString &str)
286 QStringList parts = QString(str).replace("\\", "/").split("/");
288 for(int i = 0; i < parts.count(); i++)
290 parts[i] = lamexp_clean_filename(parts[i]);
293 return parts.join("/");
297 * Get a list of all available Qt Text Codecs
299 QStringList lamexp_available_codepages(bool noAliases)
301 QStringList codecList;
303 QList<QByteArray> availableCodecs = QTextCodec::availableCodecs();
304 while(!availableCodecs.isEmpty())
306 QByteArray current = availableCodecs.takeFirst();
307 if(!(current.startsWith("system") || current.startsWith("System")))
309 codecList << QString::fromLatin1(current.constData(), current.size());
312 if(QTextCodec *currentCodec = QTextCodec::codecForName(current.constData()))
315 QList<QByteArray> aliases = currentCodec->aliases();
316 while(!aliases.isEmpty()) availableCodecs.removeAll(aliases.takeFirst());
326 * Robert Jenkins' 96 bit Mix Function
327 * Source: http://www.concentric.net/~Ttwang/tech/inthash.htm
329 static unsigned int lamexp_mix(const unsigned int x, const unsigned int y, const unsigned int z)
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);
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!
352 void lamexp_seed_rand(void)
354 qsrand(lamexp_mix(clock(), time(NULL), _getpid()));
358 * Returns a randum number
359 * Note: This function uses rand_s() if available, but falls back to qrand() otherwise
361 unsigned int lamexp_rand(void)
365 if(rand_s(&rnd) == 0)
370 for(size_t i = 0; i < sizeof(unsigned int); i++)
372 rnd = (rnd << 8) ^ qrand();
379 * Make a window blink (to draw user's attention)
381 void lamexp_blink_window(QWidget *poWindow, unsigned int count, unsigned int delay)
383 static QMutex blinkMutex;
385 const double maxOpac = 1.0;
386 const double minOpac = 0.3;
387 const double delOpac = 0.1;
389 if(!blinkMutex.tryLock())
391 qWarning("Blinking is already in progress, skipping!");
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();
401 for(unsigned int i = 0; i < count; i++)
403 for(double x = maxOpac; x >= minOpac; x -= delOpac)
405 poWindow->setWindowOpacity(x);
406 QApplication::processEvents();
410 for(double x = minOpac; x <= maxOpac; x += delOpac)
412 poWindow->setWindowOpacity(x);
413 QApplication::processEvents();
418 poWindow->setWindowOpacity(opacity);
419 QApplication::processEvents();
425 qWarning("Exception error while blinking!");
432 bool lamexp_clean_folder(const QString &folderPath)
434 QDir tempFolder(folderPath);
435 if(tempFolder.exists())
437 QFileInfoList entryList = tempFolder.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot);
439 for(int i = 0; i < entryList.count(); i++)
441 if(entryList.at(i).isDir())
443 lamexp_clean_folder(entryList.at(i).canonicalFilePath());
447 for(int j = 0; j < 3; j++)
449 if(lamexp_remove_file(entryList.at(i).canonicalFilePath()))
456 return tempFolder.rmdir(".");
462 * Computus according to H. Lichtenberg
464 static bool lamexp_computus(const QDate &date)
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);
474 int SZ = 7 - (X + X / 4 + S) % 7;
475 int OE = 7 - (OG - SZ) % 7;
480 return (date.month() == 4) && (date.day() == (OS - 31));
484 return (date.month() == 3) && (date.day() == OS);
489 * Check for Thanksgiving
491 static bool lamexp_thanksgiving(const QDate &date)
495 switch(QDate(date.year(), 11, 1).dayOfWeek())
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;
506 return (date.month() == 11) && (date.day() == day);
510 * Initialize app icon
512 QIcon lamexp_app_icon(const QDate *date, const QTime *time)
514 QDate currentDate = (date) ? QDate(*date) : QDate::currentDate();
515 QTime currentTime = (time) ? QTime(*time) : QTime::currentTime();
517 if(lamexp_thanksgiving(currentDate))
519 return QIcon(":/MainIcon6.png");
521 else if(((currentDate.month() == 12) && (currentDate.day() == 31) && (currentTime.hour() >= 20)) || ((currentDate.month() == 1) && (currentDate.day() == 1) && (currentTime.hour() <= 19)))
523 return QIcon(":/MainIcon5.png");
525 else if(((currentDate.month() == 10) && (currentDate.day() == 31) && (currentTime.hour() >= 12)) || ((currentDate.month() == 11) && (currentDate.day() == 1) && (currentTime.hour() <= 11)))
527 return QIcon(":/MainIcon4.png");
529 else if((currentDate.month() == 12) && (currentDate.day() >= 24) && (currentDate.day() <= 26))
531 return QIcon(":/MainIcon3.png");
533 else if(lamexp_computus(currentDate))
535 return QIcon(":/MainIcon2.png");
539 return QIcon(":/MainIcon1.png");
544 * Broadcast event to all windows
546 bool lamexp_broadcast(int eventType, bool onlyToVisible)
548 if(QApplication *app = dynamic_cast<QApplication*>(QApplication::instance()))
550 qDebug("Broadcasting %d", eventType);
553 QEvent poEvent(static_cast<QEvent::Type>(eventType));
554 QWidgetList list = app->topLevelWidgets();
556 while(!list.isEmpty())
558 QWidget *widget = list.takeFirst();
559 if(!onlyToVisible || widget->isVisible())
561 if(!app->sendEvent(widget, &poEvent))
568 qDebug("Broadcast %d done (%s)", eventType, (allOk ? "OK" : "Stopped"));
573 qWarning("Broadcast failed, could not get QApplication instance!");
578 ///////////////////////////////////////////////////////////////////////////////
580 ///////////////////////////////////////////////////////////////////////////////
582 extern "C" void _lamexp_global_init_utils(void)
584 LAMEXP_ZERO_MEMORY(g_lamexp_temp_folder);