1 /**
2  * Mandelbulber v2, a 3D fractal generator       ,=#MKNmMMKmmßMNWy,
3  *                                             ,B" ]L,,p%%%,,,§;, "K
4  * Copyright (C) 2014-21 Mandelbulber Team     §R-==%w["'~5]m%=L.=~5N
5  *                                        ,=mm=§M ]=4 yJKA"/-Nsaj  "Bw,==,,
6  * This file is part of Mandelbulber.    §R.r= jw",M  Km .mM  FW ",§=ß., ,TN
7  *                                     ,4R =%["w[N=7]J '"5=],""]]M,w,-; T=]M
8  * Mandelbulber is free software:     §R.ß~-Q/M=,=5"v"]=Qf,'§"M= =,M.§ Rz]M"Kw
9  * you can redistribute it and/or     §w "xDY.J ' -"m=====WeC=\ ""%""y=%"]"" §
10  * modify it under the terms of the    "§M=M =D=4"N #"%==A%p M§ M6  R' #"=~.4M
11  * GNU General Public License as        §W =, ][T"]C  §  § '§ e===~ U  !§[Z ]N
12  * published by the                    4M",,Jm=,"=e~  §  §  j]]""N  BmM"py=ßM
13  * Free Software Foundation,          ]§ T,M=& 'YmMMpM9MMM%=w=,,=MT]M m§;'§,
14  * either version 3 of the License,    TWw [.j"5=~N[=§%=%W,T ]R,"=="Y[LFT ]N
15  * or (at your option)                   TW=,-#"%=;[  =Q:["V""  ],,M.m == ]N
16  * any later version.                      J§"mr"] ,=,," =="""J]= M"M"]==ß"
17  *                                          §= "=C=4 §"eM "=B:m|4"]#F,§~
18  * Mandelbulber is distributed in            "9w=,,]w em%wJ '"~" ,=,,ß"
19  * the hope that it will be useful,                 . "K=  ,=RMMMßM"""
20  * but WITHOUT ANY WARRANTY;                            .'''
21  * without even the implied warranty
22  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23  *
24  * See the GNU General Public License for more details.
25  * You should have received a copy of the GNU General Public License
26  * along with Mandelbulber. If not, see <http://www.gnu.org/licenses/>.
27  *
28  * ###########################################################################
29  *
30  * Authors: Krzysztof Marczak (buddhi1980@gmail.com)
31  *
32  * system data - place for system functions and definition of default file paths
33  */
34 
35 #include "system.hpp"
36 
37 #include <qstylefactory.h>
38 
39 #include <clocale>
40 
41 #include <QTextStream>
42 
43 #include "global_data.hpp"
44 #include "initparameters.hpp"
45 #include "interface.hpp"
46 #include "render_window.hpp"
47 #include "system_data.hpp"
48 #include "system_directories.hpp"
49 #include "write_log.hpp"
50 
51 // custom includes
52 #ifndef _WIN32
53 #include <csignal>
54 #include <sys/ioctl.h>
55 #include <unistd.h>
56 #endif
57 #if defined(__APPLE__) || defined(__MACOSX)
58 #include "CoreFoundation/CoreFoundation.h"
59 #endif
60 
61 //#define CLSUPPORT
62 
63 sSystem systemData;
64 sActualFileNames actualFileNames;
65 
InitSystem()66 bool InitSystem()
67 {
68 	setlocale(LC_ALL, "");
69 	systemData.locale = QLocale::system();
70 	systemData.locale.setNumberOptions(QLocale::OmitGroupSeparator);
71 	QLocale::setDefault(systemData.locale);
72 
73 	QTextStream out(stdout);
74 	QTextStream outErr(stderr);
75 
76 	systemData.globalTimer.start();
77 
78 	QThreadPool::globalInstance()->setExpiryTimeout(1000);
79 
80 	systemDirectories.homeDir = QDir::toNativeSeparators(QDir::homePath() + QDir::separator());
81 #ifdef _WIN32 /* WINDOWS */
82 	systemDirectories.sharedDir = QDir::toNativeSeparators(QDir::currentPath() + QDir::separator());
83 	systemDirectories.docDir =
84 		QDir::toNativeSeparators(QDir::currentPath() + QDir::separator() + "doc" + QDir::separator());
85 #elif SHARED_DIR_IS_APP_DIR
86 	/* used for AppImage, which requires fixed data bundled at same location, as the application */
87 	QString sharePath;
88 #if defined(__APPLE__) || defined(__MACOSX)
89 	CFURLRef appUrlRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
90 	CFStringRef macPath = CFURLCopyFileSystemPath(appUrlRef, kCFURLPOSIXPathStyle);
91 	sharePath = QString::fromCFString(macPath);
92 	CFRelease(appUrlRef);
93 	CFRelease(macPath);
94 #else
95 	sharePath = QDir::currentPath();
96 #endif
97 	out << "sharePath directory: " << sharePath << endl;
98 
99 	systemDirectories.sharedDir = QDir::toNativeSeparators(sharePath + QDir::separator());
100 	systemDirectories.docDir =
101 		QDir::toNativeSeparators(sharePath + QDir::separator() + "doc" + QDir::separator());
102 #else //_WIN32
103 // if SHARED_DIR is not defined under Linux then use path relative to main executable directory
104 #ifndef SHARED_DIR
105 	QDir shareDir(QCoreApplication::applicationDirPath());
106 	bool success = false;
107 	if (shareDir.cdUp())
108 	{
109 		if (shareDir.cd("share"))
110 		{
111 			QDir shareMandelbulber = shareDir;
112 			if (shareMandelbulber.cd("mandelbulber2"))
113 			{
114 				systemDirectories.sharedDir =
115 					QDir::cleanPath(shareMandelbulber.absolutePath()) + QDir::separator();
116 				success = true;
117 			}
118 			else
119 			{
120 				outErr << "Error! There is no 'mandelbulber2' directory in "
121 							 << shareMandelbulber.absolutePath() << "\n";
122 				;
123 			}
124 
125 			QDir shareDocMandelbulber = shareDir;
126 			if (shareDocMandelbulber.cd("doc/mandelbulber2"))
127 			{
128 				systemDirectories.docDir =
129 					QDir::cleanPath(shareDocMandelbulber.absolutePath()) + QDir::separator();
130 			}
131 			else
132 			{
133 				outErr << "Error! There is no 'doc/mandelbulber2' directory in "
134 							 << shareDocMandelbulber.absolutePath() << "\n";
135 				;
136 			}
137 		}
138 		else
139 		{
140 			outErr << "Error! There is no 'share' directory in " << shareDir.absolutePath() << "\n";
141 			;
142 		}
143 	}
144 	else
145 	{
146 		outErr << "Error: " << QCoreApplication::applicationDirPath() << " has no parent directory!"
147 					 << "\n";
148 		;
149 	}
150 
151 	// try to use default share dir
152 	if (!success)
153 	{
154 		outErr << "Trying to use /usr/share/mandelbulber2 as program data directory"
155 					 << "\n";
156 		;
157 		if (shareDir.cd("/usr/share/mandelbulber2"))
158 		{
159 			systemDirectories.sharedDir = QDir::cleanPath(shareDir.absolutePath()) + QDir::separator();
160 		}
161 		else
162 		{
163 			outErr << "Error! Directory "
164 						 << "/usr/share/mandelbulber2"
165 						 << "doesn't exist!"
166 						 << "\n";
167 			;
168 		}
169 
170 		if (shareDir.cd("/usr/share/doc/mandelbulber2"))
171 		{
172 			systemDirectories.docDir = QDir::cleanPath(shareDir.absolutePath()) + QDir::separator();
173 		}
174 		else
175 		{
176 			outErr << "Error! Directory "
177 						 << "/usr/share/doc/mandelbulber2"
178 						 << "doesn't exist!"
179 						 << "\n";
180 			;
181 		}
182 	}
183 
184 #else	 // SHARED_DIR
185 	systemDirectories.sharedDir = QDir::toNativeSeparators(QString(SHARED_DIR) + QDir::separator());
186 	systemDirectories.docDir = QDir::toNativeSeparators(QString(SHARED_DOC_DIR) + QDir::separator());
187 #endif // else SHARED_DIR
188 #endif // else _WIN32
189 
190 // logfile
191 #ifdef _WIN32 /* WINDOWS */
192 	systemData.logfileName = systemDirectories.homeDir + "mandelbulber_log.txt";
193 #else
194 	systemData.logfileName = systemDirectories.homeDir + ".mandelbulber_log.txt";
195 #endif
196 	FILE *logfile = fopen(systemData.logfileName.toLocal8Bit().constData(), "w");
197 	if (logfile)
198 	{
199 		fclose(logfile);
200 	}
201 
202 	out << "Mandelbulber " << MANDELBULBER_VERSION_STRING << "\n";
203 	out << "Log file name: " << systemData.logfileName << "\n";
204 	;
205 	WriteLogString("Mandelbulber version", QString(MANDELBULBER_VERSION_STRING), 1);
206 
207 	// detecting number of CPU cores
208 	systemData.numberOfThreads = get_cpu_count();
209 
210 	printf("Detected %d CPUs\n", systemData.numberOfThreads);
211 	WriteLogDouble("CPUs detected", systemData.numberOfThreads, 2);
212 
213 #ifdef ONETHREAD // for debugging
214 	NR_THREADS = 1;
215 #endif
216 
217 // data directory location
218 #ifdef _WIN32 /* WINDOWS */
219 	systemDirectories.SetDataDirectoryHidden(
220 		systemDirectories.homeDir + "mandelbulber" + QDir::separator());
221 	systemDirectories.SetDataDirectoryPublic(
222 		systemDirectories.homeDir + "mandelbulber" + QDir::separator());
223 #else
224 	systemDirectories.SetDataDirectoryHidden(
225 		QDir::toNativeSeparators(systemDirectories.homeDir + ".mandelbulber" + QDir::separator()));
226 	systemDirectories.SetDataDirectoryPublic(
227 		QDir::toNativeSeparators(systemDirectories.homeDir + "mandelbulber" + QDir::separator()));
228 #endif
229 	out << "Program data files directory " << systemDirectories.sharedDir << "\n";
230 	;
231 	out << "Default data hidden directory: " << systemDirectories.GetDataDirectoryHidden() << "\n";
232 	;
233 	WriteLogString("Default data hidden directory", systemDirectories.GetDataDirectoryHidden(), 1);
234 	out << "Default data public directory: " << systemDirectories.GetDataDirectoryPublic() << "\n";
235 	;
236 	WriteLogString("Default data public directory", systemDirectories.GetDataDirectoryPublic(), 1);
237 
238 	//*********** temporary set to false ************
239 	systemData.noGui = false;
240 	systemData.silent = false;
241 
242 	systemData.lastSettingsFile = QDir::toNativeSeparators(
243 		systemDirectories.GetSettingsFolder() + QDir::separator() + QString("settings.fract"));
244 	systemData.lastImageFile = QDir::toNativeSeparators(
245 		systemDirectories.GetImagesFolder() + QDir::separator() + QString("image.jpg"));
246 	systemData.lastImagePaletteFile = QDir::toNativeSeparators(
247 		systemDirectories.sharedDir + "textures" + QDir::separator() + QString("colour palette.jpg"));
248 	systemData.lastGradientFile = QDir::toNativeSeparators(
249 		systemDirectories.GetGradientsFolder() + QDir::separator() + "colors.gradient");
250 
251 	QLocale systemLocale = QLocale::system();
252 	systemData.decimalPoint = systemLocale.decimalPoint();
253 	WriteLogString("Decimal point", QString(systemData.decimalPoint), 2);
254 
255 	systemData.supportedLanguages.insert("en_EN", "English");
256 	systemData.supportedLanguages.insert("pl_PL", "Polski");
257 	systemData.supportedLanguages.insert("de_DE", "Deutsch");
258 	systemData.supportedLanguages.insert("it_IT", "Italiano");
259 	systemData.supportedLanguages.insert("nl_NL", "Nederlands");
260 	systemData.supportedLanguages.insert("es_ES", "Español");
261 
262 	// get number of columns of console
263 	systemData.terminalWidth = 80;
264 	systemData.loggingVerbosity = 3;
265 	systemData.settingsLoadedFromCLI = false;
266 	systemData.globalStopRequest = false;
267 	systemData.isOutputTty = IsOutputTty();
268 
269 #ifndef WIN32
270 	handle_winch(-1);
271 #endif
272 	return true;
273 }
274 
275 // SIGWINCH is called when the window is resized.
handle_winch(int sig)276 void handle_winch(int sig)
277 {
278 	(void)sig;
279 #ifndef _WIN32
280 	signal(SIGWINCH, SIG_IGN);
281 	struct winsize w
282 	{
283 	};
284 	ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
285 	systemData.terminalWidth = w.ws_col;
286 	if (systemData.terminalWidth <= 0) systemData.terminalWidth = 80;
287 	if (systemData.terminalWidth >= 150) systemData.terminalWidth = 150;
288 	if (!systemData.isOutputTty) systemData.terminalWidth = 80;
289 	signal(SIGWINCH, handle_winch);
290 #endif
291 }
292 
get_cpu_count()293 int get_cpu_count()
294 {
295 	return QThread::idealThreadCount();
296 }
297 
CreateDefaultFolders()298 bool CreateDefaultFolders()
299 {
300 	// create data directory if not exists
301 	bool result = true;
302 
303 	result &= CreateFolder(systemDirectories.GetDataDirectoryHidden());
304 	result &= CreateFolder(systemDirectories.GetDataDirectoryPublic());
305 	result &= CreateFolder(systemDirectories.GetImagesFolder());
306 	result &= CreateFolder(systemDirectories.GetThumbnailsFolder());
307 	result &= CreateFolder(systemDirectories.GetToolbarFolder());
308 	result &= CreateFolder(systemDirectories.GetHttpCacheFolder());
309 	result &= CreateFolder(systemDirectories.GetCustomWindowStateFolder());
310 	result &= CreateFolder(systemDirectories.GetSettingsFolder());
311 	result &= CreateFolder(systemDirectories.GetSlicesFolder());
312 	result &= CreateFolder(systemDirectories.GetMaterialsFolder());
313 	result &= CreateFolder(systemDirectories.GetAnimationFolder());
314 	result &= CreateFolder(systemDirectories.GetNetrenderFolder());
315 	result &= CreateFolder(systemDirectories.GetGradientsFolder());
316 	result &= CreateFolder(systemDirectories.GetOpenCLTempFolder());
317 	result &= CreateFolder(systemDirectories.GetOpenCLCustomFormulasFolder());
318 	result &= CreateFolder(systemDirectories.GetUndoFolder());
319 	result &= PutClangFormatFileToDataDirectoryHidden();
320 
321 	RetrieveToolbarPresets(false);
322 	RetrieveExampleMaterials(false);
323 
324 #ifdef CLSUPPORT
325 	string oclDir = systemData.dataDirectory + "/custom_ocl_formulas";
326 	QString qoclDir = QString::fromStdString(oclDir);
327 	if (!QDir(qoclDir).exists())
328 	{
329 		result &= CreateFolder(oclDir);
330 		if (result)
331 		{
332 			fcopy(systemData.sharedDir + "/exampleOCLformulas/cl_example1.c", oclDir + "/cl_example1.c");
333 			fcopy(systemData.sharedDir + "/exampleOCLformulas/cl_example2.c", oclDir + "/cl_example2.c");
334 			fcopy(systemData.sharedDir + "/exampleOCLformulas/cl_example3.c", oclDir + +"/cl_example3.c");
335 			fcopy(systemData.sharedDir + "/exampleOCLformulas/cl_example1Init.c",
336 				oclDir + "/cl_example1Init.c");
337 			fcopy(systemData.sharedDir + "/exampleOCLformulas/cl_example2Init.c",
338 				oclDir + "/cl_example2Init.c");
339 			fcopy(systemData.sharedDir + "/exampleOCLformulas/cl_example3Init.c",
340 				oclDir + "/cl_example3Init.c");
341 		}
342 	}
343 #endif
344 
345 	actualFileNames.actualFilenameSettings =
346 		QString("settings") + QDir::separator() + "default.fract";
347 	actualFileNames.actualFilenameSettings =
348 		QDir::toNativeSeparators(actualFileNames.actualFilenameSettings);
349 
350 	actualFileNames.actualFilenameImage = QString("images") + QDir::separator() + "image.jpg";
351 	actualFileNames.actualFilenameImage =
352 		QDir::toNativeSeparators(actualFileNames.actualFilenameImage);
353 
354 	actualFileNames.actualFilenamePalette =
355 		systemDirectories.sharedDir + "textures" + QDir::separator() + "colour palette.jpg";
356 	actualFileNames.actualFilenamePalette =
357 		QDir::toNativeSeparators(actualFileNames.actualFilenamePalette);
358 
359 	ClearNetRenderCache();
360 	DeleteOldChache(systemDirectories.GetThumbnailsFolder(), 90);
361 	DeleteOldChache(systemDirectories.GetHttpCacheFolder(), 10);
362 
363 	return result;
364 }
365 
CreateFolder(const QString & qName)366 bool CreateFolder(const QString &qName)
367 {
368 	if (QDir(qName).exists())
369 	{
370 		WriteLogString("Directory already exists", qName, 1);
371 		return true;
372 	}
373 	else
374 	{
375 		if (QDir().mkdir(qName))
376 		{
377 			WriteLogString("Directory created", qName, 2);
378 			return true;
379 		}
380 		else
381 		{
382 			WriteLogString("Directory can't be created", qName, 1);
383 			qCritical() << "error: directory " << qName << " cannot be created";
384 			return false;
385 		}
386 	}
387 }
388 
DeleteAllFilesFromDirectory(const QString & folder,const QString & filterExpression,QRegExp::PatternSyntax pattern)389 void DeleteAllFilesFromDirectory(
390 	const QString &folder, const QString &filterExpression, QRegExp::PatternSyntax pattern)
391 {
392 	QRegExp rx(filterExpression);
393 	rx.setPatternSyntax(pattern);
394 
395 	if (QDir(folder).exists())
396 	{
397 		QDirIterator folderIterator(folder);
398 		while (folderIterator.hasNext())
399 		{
400 			folderIterator.next();
401 			if (folderIterator.fileName() == "." || folderIterator.fileName() == "..") continue;
402 			if (rx.exactMatch(folderIterator.fileName()))
403 			{
404 				if (QFile::remove(folderIterator.filePath()))
405 				{
406 					WriteLogString("File deleted", folderIterator.filePath(), 2);
407 				}
408 				else
409 				{
410 					WriteLogString("File not deleted", folderIterator.filePath(), 1);
411 				}
412 			}
413 		}
414 	}
415 	else
416 	{
417 		WriteLogString("Directory does not exist", folder, 2);
418 	}
419 }
420 
myMessageOutput(QtMsgType type,const QMessageLogContext & context,const QString & msg)421 void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
422 {
423 	QByteArray localMsg = msg.toLocal8Bit();
424 	QString text;
425 	switch (type)
426 	{
427 #if QT_VERSION >= 0x050500
428 		case QtInfoMsg:
429 			fprintf(stderr, "Info: %s\n", localMsg.constData());
430 			text = QString("Info: ") + QString(localMsg.constData());
431 			break;
432 #endif
433 		case QtDebugMsg:
434 			fprintf(stderr, "Debug: %s\n", localMsg.constData());
435 			text = QString("Debug: ") + QString(localMsg.constData());
436 			break;
437 		case QtWarningMsg:
438 			fprintf(stderr, "Warning: %s\n(%s:%u, %s)\n\n", localMsg.constData(), context.file,
439 				context.line, context.function);
440 			text = QString("Warning: ") + QString(localMsg.constData()) + " (" + context.file + ":"
441 						 + QString::number(context.line) + ", " + context.function;
442 			break;
443 		case QtCriticalMsg:
444 			fprintf(stderr, "Critical: %s\n(%s:%u, %s)\n\n", localMsg.constData(), context.file,
445 				context.line, context.function);
446 			text = QString("Critical: ") + QString(localMsg.constData()) + " (" + context.file + ":"
447 						 + QString::number(context.line) + ", " + context.function;
448 			break;
449 		case QtFatalMsg:
450 			fprintf(stderr, "Fatal: %s\n(%s:%u, %s)\n\n", localMsg.constData(), context.file,
451 				context.line, context.function);
452 			text = QString("Fatal: ") + QString(localMsg.constData()) + " (" + context.file + ":"
453 						 + QString::number(context.line) + ", " + context.function;
454 			abort();
455 			break;
456 	}
457 	WriteLog(text, 1);
458 
459 	if (type == QtFatalMsg) abort();
460 }
461 
UpdateDefaultPaths()462 void UpdateDefaultPaths()
463 {
464 	systemData.lastSettingsFile =
465 		gPar->Get<QString>("default_settings_path") + QDir::separator() + QString("settings.fract");
466 	systemData.lastImageFile =
467 		gPar->Get<QString>("default_image_path") + QDir::separator() + QString("image.jpg");
468 	gPar->Set("file_background",
469 		gPar->Get<QString>("default_textures_path") + QDir::separator() + "background.jpg");
470 	gPar->Set(
471 		"file_envmap", gPar->Get<QString>("default_textures_path") + QDir::separator() + "envmap.jpg");
472 	gPar->Set("file_lightmap",
473 		gPar->Get<QString>("default_textures_path") + QDir::separator() + "lightmap.jpg");
474 }
475 
UpdateUIStyle()476 void UpdateUIStyle()
477 {
478 	// Selecting default Fusion UI style in case if wrong style is selected
479 	if (gPar->Get<int>("ui_style_type") < 0
480 			|| gPar->Get<int>("ui_style_type") >= QStyleFactory::keys().size())
481 	{
482 		QStringList listOfStyles = QStyleFactory::keys();
483 		int indexOfFusion = listOfStyles.indexOf("Fusion");
484 		gPar->Set<int>("ui_style_type", indexOfFusion);
485 	}
486 
487 	// set ui style
488 	if (gPar->Get<int>("ui_style_type") >= 0
489 			&& gPar->Get<int>("ui_style_type") < QStyleFactory::keys().size())
490 	{
491 		QApplication::setStyle(
492 			QStyleFactory::create(QStyleFactory::keys().at(gPar->Get<int>("ui_style_type"))));
493 	}
494 }
495 
UpdateUISkin()496 void UpdateUISkin()
497 {
498 	static QPalette defaultPalette; // cache "normal" skin on first call
499 	QPalette palette;
500 
501 	QColor colorBackground1 = palette.color(QPalette::Window);
502 	QColor colorBackground2 = palette.color(QPalette::Base);
503 	QColor colorText1 = palette.color(QPalette::WindowText);
504 	QColor colorText2 = palette.color(QPalette::HighlightedText);
505 
506 	switch (gPar->Get<int>("ui_skin"))
507 	{
508 		case 1: // dark skin
509 			colorBackground1 = QColor(53, 53, 53);
510 			colorBackground2 = QColor(25, 25, 25);
511 			colorText1 = Qt::white;
512 			colorText2 = Qt::black;
513 			break;
514 		case 2: // light skin (only roughly inverted dark skin)
515 			colorBackground1 = QColor(240, 240, 240);
516 			colorBackground2 = QColor(250, 250, 250);
517 			colorText1 = Qt::black;
518 			colorText2 = Qt::white;
519 			break;
520 		case 3: // Nasa Font light
521 			QFontDatabase::addApplicationFont(":/fonts/fonts/nasalization-rg.ttf");
522 			QApplication::setFont(QFont("nasalization"));
523 			colorBackground1 = QColor(167, 173, 187);
524 			colorBackground2 = QColor(192, 196, 207);
525 			colorText1 = Qt::black;
526 			colorText2 = Qt::white;
527 			break;
528 		case 4: // Nasa Font dark
529 			QFontDatabase::addApplicationFont(":/fonts/fonts/nasalization-rg.ttf");
530 			QApplication::setFont(QFont("nasalization"));
531 			colorBackground1 = QColor(52, 61, 70);
532 			colorBackground2 = QColor(79, 91, 102);
533 			colorText1 = Qt::white;
534 			colorText2 = Qt::black;
535 			break;
536 		case 5: // Nasa Font dark green
537 			QFontDatabase::addApplicationFont(":/fonts/fonts/nasalization-rg.ttf");
538 			QApplication::setFont(QFont("nasalization"));
539 			colorBackground1 = QColor(20, 40, 10);
540 			colorBackground2 = QColor(30, 50, 0);
541 			colorText1 = Qt::green;
542 			colorText2 = Qt::black;
543 			break;
544 		case 6: // Nasa Font dark blue
545 			QFontDatabase::addApplicationFont(":/fonts/fonts/nasalization-rg.ttf");
546 			QApplication::setFont(QFont("nasalization"));
547 			colorBackground1 = QColor(10, 10, 40);
548 			colorBackground2 = QColor(20, 20, 60);
549 			colorText1 = QColor(50, 150, 255);
550 			colorText2 = Qt::black;
551 			break;
552 		case 7: // Nasa Font dark brown
553 			QFontDatabase::addApplicationFont(":/fonts/fonts/nasalization-rg.ttf");
554 			QApplication::setFont(QFont("nasalization"));
555 			colorBackground1 = QColor(40, 10, 10);
556 			colorBackground2 = QColor(60, 20, 20);
557 			colorText1 = QColor(255, 150, 50);
558 			colorText2 = Qt::black;
559 			break;
560 		default: // normal skin
561 			palette = defaultPalette;
562 			break;
563 	}
564 	if (gPar->Get<int>("ui_skin") != 0)
565 	{
566 		palette.setColor(QPalette::Window, colorBackground1);
567 		palette.setColor(QPalette::WindowText, colorText1);
568 		palette.setColor(QPalette::Base, colorBackground2);
569 		palette.setColor(QPalette::AlternateBase, colorBackground1);
570 		palette.setColor(QPalette::ToolTipBase, colorBackground2);
571 		palette.setColor(QPalette::ToolTipText, colorText1);
572 		palette.setColor(QPalette::Text, colorText1);
573 		palette.setColor(QPalette::Button, colorBackground1);
574 		palette.setColor(QPalette::ButtonText, colorText1);
575 		palette.setColor(QPalette::BrightText, Qt::red);
576 		palette.setColor(QPalette::Link, QColor(42, 130, 218));
577 
578 		palette.setColor(QPalette::Highlight, QColor(42, 130, 218));
579 		palette.setColor(QPalette::HighlightedText, colorText2);
580 	}
581 	// set ui skin
582 	QApplication::setPalette(palette);
583 }
584 
UpdateLanguage()585 void UpdateLanguage()
586 {
587 	// Set language from locale
588 	WriteLog("Prepare translator", 2);
589 	static QTranslator mandelbulberMainTranslator;
590 	static QTranslator mandelbulberFractalUiTranslator;
591 	static QTranslator qtTranslator;
592 
593 	QString locale = systemData.locale.name();
594 	if (gPar)
595 	{
596 		if (systemData.supportedLanguages.contains(gPar->Get<QString>("language")))
597 		{
598 			locale = gPar->Get<QString>("language");
599 		}
600 	}
601 
602 	WriteLogString("locale", locale, 2);
603 	mandelbulberMainTranslator.load(
604 		locale, systemDirectories.sharedDir + QDir::separator() + "language");
605 	mandelbulberFractalUiTranslator.load(
606 		"formula_" + locale, systemDirectories.sharedDir + QDir::separator() + "language");
607 
608 	WriteLog("Installing translator", 2);
609 	QCoreApplication::installTranslator(&mandelbulberMainTranslator);
610 	QCoreApplication::installTranslator(&mandelbulberFractalUiTranslator);
611 
612 	// try to load qt translator
613 	if (qtTranslator.load(
614 				QLatin1String("qt_") + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath))
615 			|| qtTranslator.load(
616 				QLatin1String("qtbase_") + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
617 	{
618 		QCoreApplication::installTranslator(&qtTranslator);
619 	}
620 }
621 
RetrieveToolbarPresets(bool force)622 void RetrieveToolbarPresets(bool force)
623 {
624 	if (QDir(systemDirectories.GetToolbarFolder())
625 					.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries)
626 					.count()
627 				== 0
628 			|| force)
629 	{
630 		// first run of program (or all toolbar items deleted) -> copy toolbar presets to working folder
631 		QDirIterator toolbarFiles(systemDirectories.sharedDir + "toolbar");
632 		while (toolbarFiles.hasNext())
633 		{
634 			toolbarFiles.next();
635 			if (toolbarFiles.fileName() == "." || toolbarFiles.fileName() == "..") continue;
636 			fcopy(toolbarFiles.filePath(),
637 				systemDirectories.GetToolbarFolder() + QDir::separator() + toolbarFiles.fileName());
638 		}
639 	}
640 }
641 
RetrieveExampleMaterials(bool force)642 void RetrieveExampleMaterials(bool force)
643 {
644 	if (QDir(systemDirectories.GetMaterialsFolder())
645 					.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries)
646 					.count()
647 				== 0
648 			|| force)
649 	{
650 		// first run of program (or all material items deleted) -> copy materials to working folder
651 		QDirIterator materialFiles(systemDirectories.sharedDir + "materials");
652 		while (materialFiles.hasNext())
653 		{
654 			materialFiles.next();
655 			if (materialFiles.fileName() == "." || materialFiles.fileName() == "..") continue;
656 			fcopy(materialFiles.filePath(),
657 				systemDirectories.GetMaterialsFolder() + QDir::separator() + materialFiles.fileName());
658 		}
659 	}
660 }
661 
CalcPreferredFontSize(bool noGui)662 void CalcPreferredFontSize(bool noGui)
663 {
664 	if (!noGui)
665 	{
666 		const int fontSize = (int)QApplication::font().pointSizeF();
667 		systemData.SetPreferredFontPointSize(fontSize);
668 
669 		QFontMetrics fm(QApplication::font());
670 		const int pixelFontSize = fm.height();
671 
672 		const int thumbnailSize = (pixelFontSize * 8);
673 		systemData.SetPreferredThumbnailSize(thumbnailSize);
674 	}
675 	else
676 	{
677 		systemData.SetPreferredFontPointSize(10);
678 		systemData.SetPreferredFontSize(10);
679 		systemData.SetPreferredThumbnailSize(80);
680 	}
681 }
682 
IsOutputTty()683 bool IsOutputTty()
684 {
685 #ifdef _WIN32 /* WINDOWS */
686 	return false;
687 #else
688 	return isatty(fileno(stdout));
689 #endif
690 }
691 
ClearNetRenderCache()692 void ClearNetRenderCache()
693 {
694 	QDir dir(systemDirectories.GetNetrenderFolder());
695 	if (dir.exists())
696 	{
697 		QDirIterator it(dir);
698 		while (it.hasNext())
699 		{
700 			QFile file(it.next());
701 			file.remove();
702 		}
703 	}
704 }
705 
DeleteOldChache(const QString & directoryPath,int maxDays)706 void DeleteOldChache(const QString &directoryPath, int maxDays)
707 {
708 	QDir dir(directoryPath);
709 	int counter = 0;
710 	qint64 totalSize = 0;
711 	if (dir.exists())
712 	{
713 		QDirIterator it(dir);
714 		while (it.hasNext())
715 		{
716 			QFile file(it.next());
717 			QFileInfo fileInfo(file);
718 			QDateTime dateTime = fileInfo.lastModified();
719 			qint64 days = dateTime.daysTo(QDateTime::currentDateTime());
720 			if (days > maxDays)
721 			{
722 				totalSize += file.size();
723 				file.remove();
724 				counter++;
725 			}
726 		}
727 	}
728 	if (counter > 0)
729 	{
730 		QString message = QString("Removed %1 old files from %2. Saved %3 MB")
731 												.arg(counter)
732 												.arg(directoryPath)
733 												.arg(totalSize / 1024 / 1024);
734 		qInfo() << message;
735 		WriteLog(message, 2);
736 	}
737 }
738 
PutClangFormatFileToDataDirectoryHidden()739 bool PutClangFormatFileToDataDirectoryHidden()
740 {
741 	QFile file(systemDirectories.GetDataDirectoryHidden() + "/.clang-format");
742 	if (file.exists()) return true;
743 	if (file.open(QIODevice::WriteOnly))
744 	{
745 		QTextStream out(&file);
746 		out << "---\n"
747 					 "Language:        Cpp\n"
748 					 "# BasedOnStyle:  LLVM\n"
749 					 "AccessModifierOffset: -2\n"
750 					 "AlignAfterOpenBracket: false\n"
751 					 "AlignEscapedNewlinesLeft: true\n"
752 					 "AlignTrailingComments: true\n"
753 					 "AlignOperands:   true\n"
754 					 "AllowAllParametersOfDeclarationOnNextLine: true\n"
755 					 "AllowShortBlocksOnASingleLine: false\n"
756 					 "AllowShortCaseLabelsOnASingleLine: true\n"
757 					 "AllowShortIfStatementsOnASingleLine: true\n"
758 					 "AllowShortLoopsOnASingleLine: false\n"
759 					 "AllowShortFunctionsOnASingleLine: Inline\n"
760 					 "AlwaysBreakAfterDefinitionReturnType: false\n"
761 					 "AlwaysBreakTemplateDeclarations: true\n"
762 					 "AlwaysBreakBeforeMultilineStrings: true\n"
763 					 "BreakBeforeBinaryOperators: NonAssignment\n"
764 					 "BreakBeforeTernaryOperators: true\n"
765 					 "BreakConstructorInitializersBeforeComma: false\n"
766 					 "BinPackParameters: true\n"
767 					 "BinPackArguments: true\n"
768 					 "ColumnLimit:     100\n"
769 					 "ConstructorInitializerAllOnOneLineOrOnePerLine: true\n"
770 					 "ConstructorInitializerIndentWidth: 4\n"
771 					 "DerivePointerAlignment: false\n"
772 					 "ExperimentalAutoDetectBinPacking: false\n"
773 					 "IndentCaseLabels: true\n"
774 					 "IndentWrappedFunctionNames: false\n"
775 					 "IndentFunctionDeclarationAfterType: false\n"
776 					 "MaxEmptyLinesToKeep: 1\n"
777 					 "KeepEmptyLinesAtTheStartOfBlocks: true\n"
778 					 "NamespaceIndentation: None\n"
779 					 "ObjCBlockIndentWidth: 2\n"
780 					 "ObjCSpaceAfterProperty: false\n"
781 					 "ObjCSpaceBeforeProtocolList: true\n"
782 					 "PenaltyBreakBeforeFirstCallParameter: 19\n"
783 					 "PenaltyBreakComment: 300\n"
784 					 "PenaltyBreakString: 1000\n"
785 					 "PenaltyBreakFirstLessLess: 120\n"
786 					 "PenaltyExcessCharacter: 1000000\n"
787 					 "PenaltyReturnTypeOnItsOwnLine: 60\n"
788 					 "PointerAlignment: Right\n"
789 					 "SpacesBeforeTrailingComments: 1\n"
790 					 "Cpp11BracedListStyle: true\n"
791 					 "Standard:        Cpp11\n"
792 					 "IndentWidth:     2\n"
793 					 "TabWidth:        2\n"
794 					 "UseTab:          Always\n"
795 					 "BreakBeforeBraces: Allman\n"
796 					 "SpacesInParentheses: false\n"
797 					 "SpacesInSquareBrackets: false\n"
798 					 "SpacesInAngles:  false\n"
799 					 "SpaceInEmptyParentheses: false\n"
800 					 "SpacesInCStyleCastParentheses: false\n"
801 					 "SpaceAfterCStyleCast: false\n"
802 					 "SpacesInContainerLiterals: true\n"
803 					 "SpaceBeforeAssignmentOperators: true\n"
804 					 "ContinuationIndentWidth: 2\n"
805 					 "CommentPragmas:  '^ IWYU pragma:'\n"
806 					 "ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]\n"
807 					 "SpaceBeforeParens: ControlStatements\n"
808 					 "DisableFormat:   false\n"
809 					 "SortIncludes: false\n"
810 					 "...\n";
811 		file.close();
812 		return true;
813 	}
814 	return false;
815 }
816