1 /**
2  * Mandelbulber v2, a 3D fractal generator       ,=#MKNmMMKmmßMNWy,
3  *                                             ,B" ]L,,p%%%,,,§;, "K
4  * Copyright (C) 2016-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  * RenderWindow class - main program window
33  *
34  * This file contains implementation of the miscellaneous slots in RenderWindow class.
35  * See also header render_window.hpp and whole implementation of class
36  * spread over render_window_*.cpp
37  */
38 
39 #include "ui_render_window.h"
40 
41 #include "animation_flight.hpp"
42 #include "animation_frames.hpp"
43 #include "animation_keyframes.hpp"
44 #include "cimage.hpp"
45 #include "global_data.hpp"
46 #include "image_scale.hpp"
47 #include "initparameters.hpp"
48 #include "interface.hpp"
49 #include "keyframes.hpp"
50 #include "material_item_model.h"
51 #include "render_window.hpp"
52 #include "settings.hpp"
53 #include "system_data.hpp"
54 #include "system_directories.hpp"
55 #include "write_log.hpp"
56 
57 #include "qt/detached_window.h"
58 #include "qt/material_editor.h"
59 #include "qt/mesh_export_dialog.h"
60 #include "qt/preferences_dialog.h"
61 #include "qt/thumbnail_widget.h"
62 #include "qt/voxel_export_dialog.h"
63 
slotResizedScrolledAreaImage(int width,int height) const64 void RenderWindow::slotResizedScrolledAreaImage(int width, int height) const
65 {
66 	if (gMainInterface->mainImage)
67 	{
68 		int selectedScale = ui->comboBox_image_preview_scale->currentIndex();
69 
70 		if (selectedScale == 0)
71 		{
72 			double scale = CalcMainImageScale(0.0, width, height, gMainInterface->mainImage);
73 			gMainInterface->mainImage->CreatePreview(scale, width, height, gMainInterface->renderedImage);
74 			gMainInterface->mainImage->UpdatePreview();
75 			gMainInterface->renderedImage->setMinimumSize(gMainInterface->mainImage->GetPreviewWidth(),
76 				gMainInterface->mainImage->GetPreviewHeight());
77 		}
78 	}
79 }
80 
slotChangedComboImageScale(int index) const81 void RenderWindow::slotChangedComboImageScale(int index) const
82 {
83 	if (gMainInterface->mainImage)
84 	{
85 		double scale = ImageScaleComboSelection2Double(index);
86 		int areaWidth = ui->scrollAreaForImage->VisibleAreaWidth();
87 		int areaHeight = ui->scrollAreaForImage->VisibleAreaHeight();
88 		scale = CalcMainImageScale(scale, areaWidth, areaHeight, gMainInterface->mainImage);
89 
90 		gMainInterface->mainImage->CreatePreview(
91 			scale, areaWidth, areaHeight, gMainInterface->renderedImage);
92 		gMainInterface->mainImage->UpdatePreview();
93 		gMainInterface->renderedImage->setMinimumSize(
94 			gMainInterface->mainImage->GetPreviewWidth(), gMainInterface->mainImage->GetPreviewHeight());
95 
96 		gPar->Set("image_preview_scale", index);
97 	}
98 }
99 
slotMouseMovedOnImage(int x,int y)100 void RenderWindow::slotMouseMovedOnImage(int x, int y)
101 {
102 	(void)x;
103 	(void)y;
104 	// qDebug() << "mouse move event";
105 	// CVector2<int> point(x, y);
106 }
107 
slotMouseClickOnImage(int x,int y,Qt::MouseButton button) const108 void RenderWindow::slotMouseClickOnImage(int x, int y, Qt::MouseButton button) const
109 {
110 	int index = ui->comboBox_mouse_click_function->currentIndex();
111 	QList<QVariant> mode = ui->comboBox_mouse_click_function->itemData(index).toList();
112 	RenderedImage::enumClickMode clickMode = RenderedImage::enumClickMode(mode.at(0).toInt());
113 
114 	switch (clickMode)
115 	{
116 		case RenderedImage::clickMoveCamera:
117 		case RenderedImage::clickFogVisibility:
118 		case RenderedImage::clickDOFFocus:
119 		case RenderedImage::clickPlaceLight:
120 		case RenderedImage::clickGetJuliaConstant:
121 		case RenderedImage::clickPlacePrimitive:
122 		case RenderedImage::clickPlaceRandomLightCenter:
123 		case RenderedImage::clickGetPoint:
124 		case RenderedImage::clickWrapLimitsAroundObject:
125 		{
126 			if (gMainInterface->renderedImage->GetEnableClickModes())
127 			{
128 				gMainInterface->SetByMouse(CVector2<double>(x, y), button, mode);
129 			}
130 			break;
131 		}
132 		case RenderedImage::clickDoNothing:
133 		case RenderedImage::clickFlightSpeedControl:
134 			// nothing
135 			break;
136 	}
137 }
138 
slotMouseDragStart(int x,int y,Qt::MouseButtons buttons)139 void RenderWindow::slotMouseDragStart(int x, int y, Qt::MouseButtons buttons)
140 {
141 	int index = ui->comboBox_mouse_click_function->currentIndex();
142 	QList<QVariant> mode = ui->comboBox_mouse_click_function->itemData(index).toList();
143 	RenderedImage::enumClickMode clickMode = RenderedImage::enumClickMode(mode.at(0).toInt());
144 
145 	switch (clickMode)
146 	{
147 		case RenderedImage::clickMoveCamera:
148 		case RenderedImage::clickPlaceLight:
149 		{
150 			if (gMainInterface->renderedImage->GetEnableClickModes())
151 			{
152 				gMainInterface->MouseDragStart(CVector2<double>(x, y), buttons, mode);
153 			}
154 			break;
155 		}
156 		default:
157 			// nothing
158 			break;
159 	}
160 }
161 
slotMouseDragFinish()162 void RenderWindow::slotMouseDragFinish()
163 {
164 	gMainInterface->MouseDragFinish();
165 }
166 
slotMouseDragDelta(int dx,int dy)167 void RenderWindow::slotMouseDragDelta(int dx, int dy)
168 {
169 	gMainInterface->MouseDragDelta(dx, dy);
170 }
171 
slotChangedComboMouseClickFunction(int index) const172 void RenderWindow::slotChangedComboMouseClickFunction(int index) const
173 {
174 	if (index >= 0) // if list is empty, then index = -1
175 	{
176 		QComboBox *comboBox = static_cast<QComboBox *>(sender());
177 		QList<QVariant> item = comboBox->itemData(index).toList();
178 		gMainInterface->renderedImage->setClickMode(item);
179 	}
180 }
181 
slotKeyPressOnImage(QKeyEvent * event)182 void RenderWindow::slotKeyPressOnImage(QKeyEvent *event)
183 {
184 	currentKeyEvents.append(event->key());
185 	lastKeyEventModifiers = event->modifiers();
186 	slotKeyHandle();
187 	buttonPressTimer->start();
188 }
189 
slotKeyReleaseOnImage(QKeyEvent * event)190 void RenderWindow::slotKeyReleaseOnImage(QKeyEvent *event)
191 {
192 	currentKeyEvents.removeOne(event->key());
193 	lastKeyEventModifiers = event->modifiers();
194 	slotKeyHandle();
195 	buttonPressTimer->stop();
196 }
197 
slotButtonLongPress()198 void RenderWindow::slotButtonLongPress()
199 {
200 	slotKeyHandle();
201 }
202 
slotKeyHandle()203 void RenderWindow::slotKeyHandle()
204 {
205 	if (currentKeyEvents.size() == 0) return;
206 
207 	gMainInterface->SynchronizeInterface(gPar, gParFractal, qInterface::read);
208 
209 	bool render = false;
210 
211 	for (int i = 0; i < currentKeyEvents.size(); i++)
212 	{
213 		int key = currentKeyEvents.at(i);
214 		Qt::KeyboardModifiers modifiers = lastKeyEventModifiers;
215 		if (modifiers & Qt::ShiftModifier)
216 		{ // Shift pressed
217 			switch (key)
218 			{
219 				case Qt::Key_Up:
220 					gMainInterface->MoveCamera("bu_move_forward");
221 					render = true;
222 					break;
223 				case Qt::Key_Down:
224 					gMainInterface->MoveCamera("bu_move_backward");
225 					render = true;
226 					break;
227 				case Qt::Key_Left:
228 					gMainInterface->MoveCamera("bu_move_left");
229 					render = true;
230 					break;
231 				case Qt::Key_Right:
232 					gMainInterface->MoveCamera("bu_move_right");
233 					render = true;
234 					break;
235 				default: break;
236 			}
237 		}
238 		else if (modifiers & Qt::ControlModifier)
239 		{ // Ctrl pressed
240 			switch (key)
241 			{
242 				case Qt::Key_Up:
243 					gMainInterface->MoveCamera("bu_move_forward");
244 					render = true;
245 					break;
246 				case Qt::Key_Down:
247 					gMainInterface->MoveCamera("bu_move_backward");
248 					render = true;
249 					break;
250 				case Qt::Key_Left:
251 					gMainInterface->RotateCamera("bu_rotate_roll_left");
252 					render = true;
253 					break;
254 				case Qt::Key_Right:
255 					gMainInterface->RotateCamera("bu_rotate_roll_right");
256 					render = true;
257 					break;
258 				default: break;
259 			}
260 		}
261 		else
262 		{
263 			// No keyboard modifiers
264 			switch (key)
265 			{
266 				case Qt::Key_W:
267 					gMainInterface->MoveCamera("bu_move_up");
268 					render = true;
269 					break;
270 				case Qt::Key_S:
271 					gMainInterface->MoveCamera("bu_move_down");
272 					render = true;
273 					break;
274 				case Qt::Key_A:
275 					gMainInterface->MoveCamera("bu_move_left");
276 					render = true;
277 					break;
278 				case Qt::Key_D:
279 					gMainInterface->MoveCamera("bu_move_right");
280 					render = true;
281 					break;
282 				case Qt::Key_Q:
283 					gMainInterface->MoveCamera("bu_move_forward");
284 					render = true;
285 					break;
286 				case Qt::Key_Z:
287 					gMainInterface->MoveCamera("bu_move_backward");
288 					render = true;
289 					break;
290 				case Qt::Key_Up:
291 					gMainInterface->RotateCamera("bu_rotate_up");
292 					render = true;
293 					break;
294 				case Qt::Key_Down:
295 					gMainInterface->RotateCamera("bu_rotate_down");
296 					render = true;
297 					break;
298 				case Qt::Key_Left:
299 					gMainInterface->RotateCamera("bu_rotate_left");
300 					render = true;
301 					break;
302 				case Qt::Key_Right:
303 					gMainInterface->RotateCamera("bu_rotate_right");
304 					render = true;
305 					break;
306 
307 				case Qt::Key_I:
308 					currentKeyEvents.removeOne(key); // long press not allowed
309 					gKeyframeAnimation->slotAddKeyframe();
310 					break;
311 				case Qt::Key_M:
312 					currentKeyEvents.removeOne(key); // long press not allowed
313 					gKeyframeAnimation->slotModifyKeyframe();
314 					break;
315 				/*case Qt::Key_D:
316 				 *		currentKeyEvents.removeOne(key); // long press not allowed
317 				 *		gKeyframeAnimation->slotDeleteKeyframe();
318 				 * break;*/
319 				case Qt::Key_N:
320 					currentKeyEvents.removeOne(key); // long press not allowed
321 					gKeyframeAnimation->slotIncreaseCurrentTableIndex();
322 					break;
323 				case Qt::Key_P:
324 					currentKeyEvents.removeOne(key); // long press not allowed
325 					gKeyframeAnimation->slotDecreaseCurrentTableIndex();
326 					break;
327 				default: break;
328 			}
329 		}
330 	}
331 	gMainInterface->SynchronizeInterface(gPar, gParFractal, qInterface::write);
332 
333 	if (render)
334 	{
335 		gMainInterface->StartRender();
336 	}
337 }
338 
339 // global shortcuts
keyPressEvent(QKeyEvent * event)340 void RenderWindow::keyPressEvent(QKeyEvent *event)
341 {
342 	int key = event->key();
343 	Qt::KeyboardModifiers modifiers = event->modifiers();
344 
345 	if (modifiers & Qt::ShiftModifier)
346 	{
347 		switch (key)
348 		{
349 			case Qt::Key_Escape: gMainInterface->GlobalStopRequest(); break;
350 			default: break;
351 		}
352 	}
353 }
354 
slotMouseWheelRotatedWithKeyOnImage(int x,int y,int delta,Qt::KeyboardModifiers keyModifiers) const355 void RenderWindow::slotMouseWheelRotatedWithKeyOnImage(
356 	int x, int y, int delta, Qt::KeyboardModifiers keyModifiers) const
357 {
358 	if (gMainInterface->renderedImage->GetEnableClickModes())
359 	{
360 		int index = ui->comboBox_mouse_click_function->currentIndex();
361 		QList<QVariant> mode = ui->comboBox_mouse_click_function->itemData(index).toList();
362 		RenderedImage::enumClickMode clickMode = RenderedImage::enumClickMode(mode.at(0).toInt());
363 		switch (clickMode)
364 		{
365 			case RenderedImage::clickPlaceLight:
366 			{
367 				if (keyModifiers & Qt::AltModifier)
368 				{
369 					double deltaLog = exp(delta * 0.001);
370 					double dist = ui->widgetEffects->GetAuxLightManualPlacementDistance();
371 					dist *= deltaLog;
372 					ui->widgetEffects->slotSetAuxLightManualPlacementDistance(dist);
373 				}
374 				else if (keyModifiers & Qt::ControlModifier)
375 				{
376 					gMainInterface->MoveLightByWheel(delta);
377 				}
378 				break;
379 			}
380 			case RenderedImage::clickMoveCamera:
381 			{
382 				Qt::MouseButton button = (delta > 0) ? Qt::LeftButton : Qt::RightButton;
383 				mode.append(QVariant(delta));
384 				gMainInterface->SetByMouse(CVector2<double>(x, y), button, mode);
385 				break;
386 			}
387 			default: break;
388 		}
389 	}
390 }
391 
slotChangedCheckBoxCursorVisibility(int state)392 void RenderWindow::slotChangedCheckBoxCursorVisibility(int state)
393 {
394 	gMainInterface->renderedImage->SetCursorVisibility(state);
395 }
396 
397 // adds dynamic actions to the toolbar (example settings)
slotPopulateToolbar(bool completeRefresh)398 void RenderWindow::slotPopulateToolbar(bool completeRefresh)
399 {
400 	WriteLog("cInterface::PopulateToolbar(QWidget *window, QToolBar *toolBar) started", 2);
401 	QDir toolbarDir = QDir(gPar->Get<QString>("toolbar_path"));
402 	toolbarDir.setSorting(QDir::Time);
403 	QStringList toolbarFiles = toolbarDir.entryList(QDir::NoDotAndDotDot | QDir::Files);
404 	QSignalMapper *mapPresetsFromExamplesLoad = new QSignalMapper(this);
405 	QSignalMapper *mapPresetsFromExamplesRemove = new QSignalMapper(this);
406 	ui->toolBar->setIconSize(
407 		QSize(gPar->Get<int>("toolbar_icon_size"), gPar->Get<int>("toolbar_icon_size")));
408 
409 	QList<QAction *> actions = ui->toolBar->actions();
410 	QStringList toolbarInActions;
411 
412 	QApplication::connect(
413 		mapPresetsFromExamplesLoad, SIGNAL(mapped(QString)), this, SLOT(slotMenuLoadPreset(QString)));
414 
415 	for (auto action : actions)
416 	{
417 		if (action->objectName() == "actionAdd_Settings_to_Toolbar") continue;
418 		if (!toolbarFiles.contains(action->objectName()) || completeRefresh)
419 		{
420 			// preset has been removed
421 			ui->toolBar->removeAction(action);
422 		}
423 		else
424 		{
425 			toolbarInActions << action->objectName();
426 		}
427 	}
428 
429 	for (int i = 0; i < toolbarFiles.size(); i++)
430 	{
431 		if (systemData.globalStopRequest) break;
432 
433 		if (toolbarInActions.contains(toolbarFiles.at(i)))
434 		{
435 			// already present
436 			continue;
437 		}
438 		QString filename = gPar->Get<QString>("toolbar_path") + QDir::separator() + toolbarFiles.at(i);
439 		cThumbnailWidget *thumbWidget = nullptr;
440 
441 		if (QFileInfo(filename).suffix() == QString("fract"))
442 		{
443 			WriteLogString("Generating thumbnail for preset", filename, 2);
444 			cSettings parSettings(cSettings::formatFullText);
445 			parSettings.BeQuiet(true);
446 
447 			if (parSettings.LoadFromFile(filename))
448 			{
449 				std::shared_ptr<cParameterContainer> par(new cParameterContainer);
450 				std::shared_ptr<cFractalContainer> parFractal(new cFractalContainer);
451 				InitParams(par);
452 				for (int j = 0; j < NUMBER_OF_FRACTALS; j++)
453 					InitFractalParams(parFractal->at(j));
454 
455 				/****************** TEMPORARY CODE FOR MATERIALS *******************/
456 
457 				InitMaterialParams(1, par);
458 
459 				/*******************************************************************/
460 
461 				if (parSettings.Decode(par, parFractal))
462 				{
463 					thumbWidget = new cThumbnailWidget(
464 						gPar->Get<int>("toolbar_icon_size"), gPar->Get<int>("toolbar_icon_size"), 2, this);
465 					thumbWidget->UseOneCPUCore(true);
466 					par->Set("opencl_mode", gPar->Get<int>("opencl_mode"));
467 					par->Set("opencl_enabled", gPar->Get<bool>("opencl_enabled"));
468 					if (!gPar->Get<bool>("thumbnails_with_opencl")) par->Set("opencl_enabled", false);
469 					thumbWidget->AssignParameters(par, parFractal);
470 				}
471 			}
472 		}
473 
474 		if (thumbWidget)
475 		{
476 			QWidgetAction *action = new QWidgetAction(this);
477 			QToolButton *buttonLoad = new QToolButton;
478 			QVBoxLayout *tooltipLayout = new QVBoxLayout;
479 			QToolButton *buttonRemove = new QToolButton;
480 
481 			tooltipLayout->setContentsMargins(3, 3, 3, 3);
482 			tooltipLayout->addWidget(thumbWidget);
483 			QIcon iconDelete = QIcon::fromTheme("list-remove", QIcon(":system/icons/list-remove.svg"));
484 			buttonRemove->setIcon(iconDelete);
485 			buttonRemove->setMaximumSize(QSize(15, 15));
486 			buttonRemove->setStyleSheet("margin-bottom: -2px; margin-left: -2px;");
487 			tooltipLayout->addWidget(buttonRemove);
488 			buttonLoad->setToolTip(QObject::tr("Toolbar settings: ") + filename);
489 			buttonLoad->setLayout(tooltipLayout);
490 			action->setDefaultWidget(buttonLoad);
491 			action->setObjectName(toolbarFiles.at(i));
492 			ui->toolBar->addAction(action);
493 
494 			mapPresetsFromExamplesLoad->setMapping(buttonLoad, filename);
495 			mapPresetsFromExamplesRemove->setMapping(buttonRemove, filename);
496 			QApplication::connect(buttonLoad, SIGNAL(clicked()), mapPresetsFromExamplesLoad, SLOT(map()));
497 			QApplication::connect(
498 				buttonRemove, SIGNAL(clicked()), mapPresetsFromExamplesRemove, SLOT(map()));
499 		}
500 		QApplication::processEvents();
501 	}
502 
503 	QApplication::connect(mapPresetsFromExamplesRemove, SIGNAL(mapped(QString)), this,
504 		SLOT(slotMenuRemovePreset(QString)));
505 
506 	WriteLog("cInterface::PopulateToolbar(QWidget *window, QToolBar *toolBar) finished", 2);
507 }
508 
slotPresetAddToToolbar()509 void RenderWindow::slotPresetAddToToolbar()
510 {
511 	cSettings parSettings(cSettings::formatCondensedText);
512 	gMainInterface->SynchronizeInterface(gPar, gParFractal, qInterface::read);
513 	parSettings.CreateText(gPar, gParFractal, gAnimFrames, gKeyframes);
514 	QString filename =
515 		systemDirectories.GetToolbarFolder() + QDir::separator() + parSettings.GetHashCode() + ".fract";
516 	parSettings.SaveToFile(filename);
517 	slotPopulateToolbar();
518 }
519 
slotMenuLoadPreset(QString filename)520 void RenderWindow::slotMenuLoadPreset(QString filename)
521 {
522 	cSettings parSettings(cSettings::formatFullText);
523 	gMainInterface->DisablePeriodicRefresh();
524 	parSettings.LoadFromFile(filename);
525 	gInterfaceReadyForSynchronization = false;
526 	parSettings.Decode(gPar, gParFractal, gAnimFrames, gKeyframes);
527 	gMainInterface->RebuildPrimitives(gPar);
528 	gMainInterface->materialListModel->Regenerate();
529 	ui->widgetEffects->RegenerateLights();
530 
531 	gMainInterface->SynchronizeInterface(gPar, gParFractal, qInterface::write);
532 	gInterfaceReadyForSynchronization = true;
533 
534 	gMainInterface->ComboMouseClickUpdate();
535 	systemData.lastSettingsFile = gPar->Get<QString>("default_settings_path") + QDir::separator()
536 																+ QFileInfo(filename).fileName();
537 
538 	gFlightAnimation->RefreshTable();
539 	gKeyframeAnimation->RefreshTable();
540 	showDescriptionPopup();
541 	setWindowTitle(QString("Mandelbulber (") + systemData.lastSettingsFile + ")");
542 }
543 
slotMenuRemovePreset(QString filename)544 void RenderWindow::slotMenuRemovePreset(QString filename)
545 {
546 	QFile::remove(filename);
547 	slotPopulateToolbar();
548 }
549 
550 // adds dynamic actions to the view > window states
slotPopulateCustomWindowStates(bool completeRefresh)551 void RenderWindow::slotPopulateCustomWindowStates(bool completeRefresh)
552 {
553 	WriteLog("cInterface::slotPopulateCustomWindowStates() started", 2);
554 	QDir customWindowStateDir = QDir(systemDirectories.GetCustomWindowStateFolder());
555 	customWindowStateDir.setSorting(QDir::Time);
556 	QStringList geometryFileExtension("*.geometry");
557 	customWindowStateDir.setNameFilters(geometryFileExtension);
558 	QStringList customWindowStateFiles =
559 		customWindowStateDir.entryList(QDir::NoDotAndDotDot | QDir::Files);
560 	QSignalMapper *mapCustomWindowLoad = new QSignalMapper(this);
561 	// QSignalMapper *mapCustomWindowRemove = new QSignalMapper(this);
562 
563 	QList<QAction *> actions = ui->menuSaved_window_layouts->actions();
564 	QStringList customWindowActions;
565 	for (auto action : actions)
566 	{
567 		if (!action->objectName().startsWith("window_")) continue;
568 		if (!customWindowStateFiles.contains(action->objectName()) || completeRefresh)
569 		{
570 			// preset has been removed
571 			ui->menuSaved_window_layouts->removeAction(action);
572 		}
573 		else
574 		{
575 			customWindowActions << action->objectName();
576 		}
577 	}
578 
579 	for (int i = 0; i < customWindowStateFiles.size(); i++)
580 	{
581 		QString customWindowStateGeometryFile = customWindowStateFiles.at(i);
582 		QString customWindowStateFile = customWindowStateGeometryFile.replace(".geometry", "");
583 		if (customWindowActions.contains("window_" + customWindowStateFile))
584 		{
585 			// already present
586 			continue;
587 		}
588 		QString filename =
589 			systemDirectories.GetCustomWindowStateFolder() + QDir::separator() + customWindowStateFile;
590 
591 		/*
592 		QWidgetAction *action = new QWidgetAction(this);
593 		QLabel *buttonLoad = new QLabel;
594 		QHBoxLayout *tooltipLayout = new QHBoxLayout;
595 		QToolButton *buttonRemove = new QToolButton;
596 		QLabel *label = new QLabel;
597 
598 		buttonLoad->setStyleSheet("QLabel{ border: none; margin: 2px; padding: 1px; }");
599 		label->setText(QByteArray().fromBase64(QByteArray().append(customWindowStateFile)));
600 		tooltipLayout->setContentsMargins(5, 0, 0, 0);
601 		QIcon iconDelete = QIcon::fromTheme("list-remove", QIcon(":system/icons/list-remove.svg"));
602 		buttonRemove->setIcon(iconDelete);
603 		buttonRemove->setMaximumSize(QSize(15, 15));
604 		tooltipLayout->addWidget(buttonRemove);
605 		tooltipLayout->addWidget(label);
606 		buttonLoad->setLayout(tooltipLayout);
607 		// FIXME - when minimum width is not specified the widget is very small
608 		buttonLoad->setMinimumWidth(300);
609 		action->setDefaultWidget(buttonLoad);*/
610 
611 		QAction *action = new QAction(this);
612 		action->setText(QByteArray().fromBase64(QByteArray().append(customWindowStateFile.toUtf8())));
613 		action->setObjectName("window_" + customWindowStateFile);
614 
615 		ui->menuSaved_window_layouts->addAction(action);
616 
617 		mapCustomWindowLoad->setMapping(action, filename);
618 		QApplication::connect(action, SIGNAL(triggered()), mapCustomWindowLoad, SLOT(map()));
619 		QApplication::processEvents();
620 	}
621 	QApplication::connect(mapCustomWindowLoad, SIGNAL(mapped(QString)), this,
622 		SLOT(slotMenuLoadCustomWindowState(QString)));
623 
624 	/*QApplication::connect(mapCustomWindowRemove, SIGNAL(mapped(QString)), this,
625 		SLOT(slotMenuRemoveCustomWindowState(QString)));*/
626 
627 	WriteLog("cInterface::slotPopulateCustomWindowStates() finished", 2);
628 }
629 
slotCustomWindowStateAddToMenu()630 void RenderWindow::slotCustomWindowStateAddToMenu()
631 {
632 	bool ok;
633 	QString text = QInputDialog::getText(this, tr("Add window settings"),
634 		tr("Enter a name for the new window settings"), QLineEdit::Normal, "", &ok);
635 	if (!ok || text.isEmpty())
636 	{
637 		WriteLogCout("Cancelled window saving", 2);
638 		return;
639 	}
640 	QString textEncoded = QByteArray().append(text.toUtf8()).toBase64();
641 	QString basePath = systemDirectories.GetCustomWindowStateFolder() + QDir::separator();
642 	QString filename = basePath + textEncoded;
643 	QString filenameGeometry = filename + ".geometry";
644 	QString filenameState = filename + ".state";
645 	QFile fileGeometry(filenameGeometry);
646 	QFile fileState(filenameState);
647 	if (!fileGeometry.open(QIODevice::WriteOnly) || !fileState.open(QIODevice::WriteOnly))
648 	{
649 		qWarning() << "Could not open output files: " << filename << ".[geometry,state]";
650 		return;
651 	}
652 	fileGeometry.write(saveGeometry());
653 	fileState.write(saveState());
654 	fileGeometry.close();
655 	fileState.close();
656 
657 	slotPopulateCustomWindowStates();
658 }
659 
slotMenuLoadCustomWindowState(QString filename)660 void RenderWindow::slotMenuLoadCustomWindowState(QString filename)
661 {
662 	QFile fileGeometry(filename + ".geometry");
663 	QFile fileState(filename + ".state");
664 	if (!fileGeometry.open(QIODevice::ReadOnly) || !fileState.open(QIODevice::ReadOnly))
665 	{
666 		qWarning() << "Could not open input files: " << filename << ".[geometry,state]";
667 		return;
668 	}
669 	// restoreGeometry(fileGeometry.readAll());
670 	restoreState(fileState.readAll());
671 }
672 
slotCustomWindowRemovePopup()673 void RenderWindow::slotCustomWindowRemovePopup()
674 {
675 	QDir customWindowStateDir = QDir(systemDirectories.GetCustomWindowStateFolder());
676 	customWindowStateDir.setSorting(QDir::Time);
677 	QStringList geometryFileExtension("*.geometry");
678 	customWindowStateDir.setNameFilters(geometryFileExtension);
679 	QStringList customWindowStateFiles =
680 		customWindowStateDir.entryList(QDir::NoDotAndDotDot | QDir::Files);
681 	QStringList itemsEscaped;
682 	if (customWindowStateFiles.size() == 0)
683 	{
684 		WriteLogCout("Nothing to remove", 2);
685 		return;
686 	}
687 	for (int i = 0; i < customWindowStateFiles.size(); i++)
688 	{
689 		QString customWindowStateFile = customWindowStateFiles[i].replace(".geometry", "");
690 		itemsEscaped.append(
691 			QByteArray().fromBase64(QByteArray().append(customWindowStateFile.toUtf8())));
692 	}
693 	bool ok;
694 
695 	QString itemEscaped = QInputDialog::getItem(this, tr("Remove window settings"),
696 		tr("Select window setting to remove"), itemsEscaped, 0, false, &ok);
697 	if (!ok || itemEscaped.isEmpty())
698 	{
699 		WriteLogCout("Cancelled window removing", 2);
700 		return;
701 	}
702 	int index = itemsEscaped.indexOf(itemEscaped);
703 	QString filename = systemDirectories.GetCustomWindowStateFolder() + QDir::separator()
704 										 + customWindowStateFiles.at(index);
705 	slotMenuRemoveCustomWindowState(filename.replace(".geometry", ""));
706 }
707 
slotMenuRemoveCustomWindowState(QString filename)708 void RenderWindow::slotMenuRemoveCustomWindowState(QString filename)
709 {
710 	QFile::remove(filename + ".geometry");
711 	QFile::remove(filename + ".state");
712 	slotPopulateCustomWindowStates();
713 }
714 
slotPopulateRecentSettings(bool completeRefresh)715 void RenderWindow::slotPopulateRecentSettings(bool completeRefresh)
716 {
717 	WriteLog("cInterface::slotPopulateRecentSettings() started", 2);
718 	QFile recentFilesFile(systemDirectories.GetRecentFilesListFile());
719 	if (!recentFilesFile.open(QFile::ReadOnly | QFile::Text))
720 	{
721 		// qDebug() << "cannot open recent file";
722 		return;
723 	}
724 	QTextStream in(&recentFilesFile);
725 	QString recentFilesFileContent = in.readAll();
726 
727 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
728 	QStringList recentFiles =
729 		recentFilesFileContent.split(QRegExp("\n|\r\n|\r"), QString::KeepEmptyParts);
730 #else
731 	QStringList recentFiles = recentFilesFileContent.split(QRegExp("\n|\r\n|\r"), Qt::KeepEmptyParts);
732 #endif
733 
734 	QSignalMapper *mapRecentFileLoad = new QSignalMapper(this);
735 	QList<QAction *> actions = ui->menuRecent_Settings_list->actions();
736 	QStringList recentFileInActions;
737 	for (auto action : actions)
738 	{
739 		if (!action->objectName().startsWith("recent_")) continue;
740 		if (!recentFileInActions.contains(action->objectName()) || completeRefresh)
741 		{
742 			// preset has been removed
743 			ui->menuRecent_Settings_list->removeAction(action);
744 		}
745 		else
746 		{
747 			recentFileInActions << action->objectName();
748 		}
749 	}
750 
751 	for (int i = 0; i < recentFiles.size(); i++)
752 	{
753 		QString settingsFile = recentFiles.at(i);
754 		if (recentFileInActions.contains("recent_" + settingsFile))
755 		{
756 			// already present
757 			continue;
758 		}
759 
760 		QAction *action = new QAction(this);
761 		action->setText(QFileInfo(settingsFile).baseName());
762 		action->setObjectName("recent_" + settingsFile);
763 
764 		ui->menuRecent_Settings_list->addAction(action);
765 
766 		mapRecentFileLoad->setMapping(action, settingsFile);
767 		QApplication::connect(action, SIGNAL(triggered()), mapRecentFileLoad, SLOT(map()));
768 		QApplication::processEvents();
769 	}
770 	QApplication::connect(
771 		mapRecentFileLoad, SIGNAL(mapped(QString)), this, SLOT(slotMenuLoadSettingsFromFile(QString)));
772 
773 	WriteLog("cInterface::slotPopulateRecentSettings() finished", 2);
774 }
775 
slotQuit()776 void RenderWindow::slotQuit()
777 {
778 	gMainInterface->QuitApplicationDialog();
779 }
780 
closeEvent(QCloseEvent * event)781 void RenderWindow::closeEvent(QCloseEvent *event)
782 {
783 	if (gMainInterface->QuitApplicationDialog())
784 	{
785 		event->accept();
786 	}
787 	else
788 	{
789 		event->ignore();
790 	}
791 }
792 
changeEvent(QEvent * event)793 void RenderWindow::changeEvent(QEvent *event)
794 {
795 	if (event->type() == QEvent::LanguageChange)
796 	{
797 		// retranslate designer form (single inheritance approach)
798 		// ui->retranslateUi(this);
799 	}
800 
801 	// remember to call base class implementation
802 	QMainWindow::changeEvent(event);
803 }
804 
slotUpdateProgressAndStatus(const QString & text,const QString & progressText,double progress,cProgressText::enumProgressType progressType) const805 void RenderWindow::slotUpdateProgressAndStatus(const QString &text, const QString &progressText,
806 	double progress, cProgressText::enumProgressType progressType) const
807 {
808 	ui->statusbar->showMessage(text, 0);
809 	MyProgressBar *progressBar = nullptr;
810 	bool isQueue = false;
811 
812 	// FIXME: sender can be deleted by another thread (e.g. ImageFileSave exists very short time)
813 	//  if (sender())
814 	//  {
815 	//     isQueue = sender()->objectName() == "Queue";
816 	//  }
817 
818 	switch (progressType)
819 	{
820 		case cProgressText::progress_IMAGE:
821 			if (isQueue)
822 				progressBar = gMainInterface->progressBarQueueImage;
823 			else
824 				progressBar = gMainInterface->progressBar;
825 			break;
826 		case cProgressText::progress_ANIMATION:
827 			if (isQueue)
828 				progressBar = gMainInterface->progressBarQueueAnimation;
829 			else
830 				progressBar = gMainInterface->progressBarAnimation;
831 			break;
832 		case cProgressText::progress_QUEUE:
833 			// nothing to be done, no progress bar for queue in GUI
834 			break;
835 	}
836 
837 	if (progressBar)
838 	{
839 		if (!progressBar->isVisible())
840 		{
841 			progressBar->setVisible(true);
842 		}
843 		progressBar->setValue(progress * 1000.0);
844 		progressBar->setTextVisible(true);
845 		progressBar->setFormat(progressText);
846 	}
847 }
848 
slotUpdateProgressHide(cProgressText::enumProgressType progressType)849 void RenderWindow::slotUpdateProgressHide(cProgressText::enumProgressType progressType)
850 {
851 	MyProgressBar *progressBar = nullptr;
852 	switch (progressType)
853 	{
854 		case cProgressText::progress_IMAGE: progressBar = gMainInterface->progressBar; break;
855 		case cProgressText::progress_ANIMATION:
856 			progressBar = gMainInterface->progressBarAnimation;
857 			break;
858 		case cProgressText::progress_QUEUE:
859 			// nothing to be done, no progress bar for queue in GUI
860 			break;
861 	}
862 
863 	if (progressBar)
864 	{
865 		if (progressBar->isVisible()) progressBar->setVisible(false);
866 	}
867 }
868 
slotMenuProgramPreferences()869 void RenderWindow::slotMenuProgramPreferences()
870 {
871 	if (!preferencesDialog)
872 	{
873 		preferencesDialog = new cPreferencesDialog;
874 	}
875 
876 	preferencesDialog->show();
877 	preferencesDialog->raise();
878 	preferencesDialog->activateWindow();
879 }
880 
slotExportVoxelLayers()881 void RenderWindow::slotExportVoxelLayers()
882 {
883 	if (!voxelExportDialog)
884 	{
885 		voxelExportDialog = new cVoxelExportDialog;
886 	}
887 
888 	voxelExportDialog->show();
889 	voxelExportDialog->raise();
890 	voxelExportDialog->activateWindow();
891 }
892 
slotExportMesh()893 void RenderWindow::slotExportMesh()
894 {
895 	if (!meshExportDialog)
896 	{
897 		meshExportDialog = new cMeshExportDialog;
898 	}
899 
900 	meshExportDialog->show();
901 	meshExportDialog->raise();
902 	meshExportDialog->activateWindow();
903 }
904 
slotQuestionMessage(const QString & questionTitle,const QString & questionText,QMessageBox::StandardButtons buttons,QMessageBox::StandardButton * reply) const905 void RenderWindow::slotQuestionMessage(const QString &questionTitle, const QString &questionText,
906 	QMessageBox::StandardButtons buttons, QMessageBox::StandardButton *reply) const
907 {
908 	*reply = QMessageBox::question(ui->centralwidget, questionTitle, questionText, buttons);
909 }
910 
slotAutoRefresh()911 void RenderWindow::slotAutoRefresh()
912 {
913 	gMainInterface->PeriodicRefresh();
914 }
915 
slotMaterialSelected(int matIndex) const916 void RenderWindow::slotMaterialSelected(int matIndex) const
917 {
918 	gMainInterface->MaterialSelected(matIndex);
919 }
920 
slotMaterialEdited()921 void RenderWindow::slotMaterialEdited()
922 {
923 	SynchronizeInterfaceWindow(gMainInterface->materialEditor, gPar, qInterface::write);
924 }
925 
slotChangedComboGridType(int index)926 void RenderWindow::slotChangedComboGridType(int index)
927 {
928 	gMainInterface->renderedImage->SetGridType(RenderedImage::enumGridType(index));
929 }
930 
ResetGlobalStopRequest()931 void RenderWindow::ResetGlobalStopRequest()
932 {
933 	gMainInterface->ResetGlobalStopRequest();
934 }
935 
eventFilter(QObject * obj,QEvent * event)936 bool RenderWindow::eventFilter(QObject *obj, QEvent *event)
937 {
938 
939 	if (event->type() == QEvent::ToolTip && !gPar->Get<bool>("display_tooltips"))
940 	{
941 		{
942 			return true;
943 		}
944 	}
945 	else if (event->type() == QEvent::ShortcutOverride)
946 	{
947 		buttonPressTimer->stop();
948 		return QMainWindow::eventFilter(obj, event);
949 	}
950 	else
951 	{
952 		return QMainWindow::eventFilter(obj, event);
953 	}
954 }
955 
ToggleFullScreen()956 void RenderWindow::ToggleFullScreen()
957 {
958 	if (!ui->actionDetach_image_from_main_window->isChecked())
959 	{
960 		gMainInterface->DetachMainImageWidget();
961 		gMainInterface->detachedWindow->showFullScreen();
962 		ui->actionDetach_image_from_main_window->setChecked(true);
963 	}
964 	else
965 	{
966 		gMainInterface->AttachMainImageWidget();
967 		ui->actionDetach_image_from_main_window->setChecked(false);
968 	}
969 }
970