1 #include <QFileDialog>
2 #include <QProgressBar>
3 #include <QTextBrowser>
4 #include <QTextStream>
5 #include <QApplication>
6 #include <QStatusBar>
7 #include <QPainter>
8 #include <QMessageBox>
9 #include <QDateTime>
10 #include <QClipboard>
11 #include <QProcess>
12 #include <QProgressDialog>
13 #include <QTimer>
14 #include <QtGlobal>
15 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
16 #include <QScreen>
17 #else
18 #include <QDesktopWidget>
19 #endif
20 #include <QInputDialog>
21 #include <QPrintDialog>
22 #include <QDesktopServices>
23 #include <QPrinter>
24 #include <QFileInfo>
25 #ifdef __APPLE__
26 #include <CoreFoundation/CoreFoundation.h>
27 #include <CoreServices/CoreServices.h>
28 #include <ApplicationServices/ApplicationServices.h>
29 #endif // __APPLE__
30 #include "misc.h"
31 
32 #include <TCFoundation/TCAutoreleasePool.h>
33 #include <TCFoundation/mystring.h>
34 #include <TCFoundation/TCStringArray.h>
35 #include <TCFoundation/TCAlertManager.h>
36 #include <TCFoundation/TCProgressAlert.h>
37 #include <TCFoundation/TCMacros.h>
38 #include <TCFoundation/TCLocalStrings.h>
39 #include <TCFoundation/TCWebClient.h>
40 #include <LDLoader/LDLError.h>
41 #include <LDLoader/LDLModel.h>
42 #include <LDLib/LDrawModelViewer.h>
43 #include <LDLib/LDInputHandler.h>
44 //#include <LDLib/ModelMacros.h>
45 #include <TRE/TREMainModel.h>
46 #include <TRE/TREGLExtensions.h>
47 #include "About.h"
48 #include "Help.h"
49 #include "LDViewMainWindow.h"
50 #include "LDViewErrors.h"
51 #include "LDViewModelTree.h"
52 #include "LDViewExtraDir.h"
53 #include "LDViewExportOption.h"
54 #include "LDViewSnapshotSettings.h"
55 #include "LDViewPartList.h"
56 #include <TCFoundation/TCUserDefaults.h>
57 #include "LDLib/LDUserDefaultsKeys.h"
58 #include <LDLib/LDPartsList.h>
59 #include <assert.h>
60 
61 #include "ModelViewerWidget.h"
62 #include "AlertHandler.h"
63 #include <LDLib/LDConsoleAlertHandler.h>
64 
65 #define POLL_INTERVAL 500
66 
67 #define PNG_IMAGE_TYPE_INDEX 1
68 #define BMP_IMAGE_TYPE_INDEX 2
69 #define JPG_IMAGE_TYPE_INDEX 3
70 #define WIN_WIDTH 640
71 #define WIN_HEIGHT 480
72 
73 
ModelViewerWidget(QWidget * parent)74 ModelViewerWidget::ModelViewerWidget(QWidget *parent)
75 #if (QT_VERSION >= 0x50400) && defined(QOPENGLWIDGET)
76 	:QOpenGLWidget(parent),
77 #else
78 	:QGLWidget(parent),
79 #endif
80     modeltree(new LDViewModelTree(parent,preferences,this)),
81 	cameralocation(new CameraLocation(parent, this)),
82 	rotationcenter(new RotationCenter(parent, this)),
83     boundingbox(new BoundingBox(parent, this)),
84     mpdmodel(new MpdModel(parent,this)),
85 	modelViewer(new LDrawModelViewer(100, 100)),
86 	snapshotTaker(NULL),
87 	lastX(-1),
88 	lastY(-1),
89 	originalZoomY(-1),
90 	rotationSpeed(0.0f),
91 	viewMode(LDInputHandler::VMExamine),
92 	spinButton(1),
93 	zoomButton(2),
94 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
95 	fbo(NULL),
96 #endif
97     preferences(NULL),
98     extradir(NULL),
99     snapshotsettings(NULL),
100     jpegoptions(NULL),
101     extensionsPanel(NULL),
102 	latitudelongitude(new LatitudeLongitude(parent, this)),
103 	aboutPanel(NULL),
104 	helpContents(NULL),
105 	mainWindow(NULL),
106 	statusBar(NULL),
107 	progressBar(NULL),
108 	progressLabel(NULL),
109 	progressLatlong(NULL),
110 	progressMode(NULL),
111 	loading(false),
112 	saving(false),
113 	printing(false),
114 	cancelLoad(false),
115 	app(NULL),
116 	painting(false),
117 	fps(-1.0),
118 	numFramesSinceReference(0),
119 	paintTimer(0),
120 	pollTimer(0),
121 	loadTimer(0),
122 	libraryUpdateTimer(0),
123 	saveDialog(NULL),
124 	errors(new LDViewErrors(this, preferences)),
125 	lastFileSize(0),
126 	fileInfo(NULL),
127 	lockCount(0),
128 	fullscreen(0),
129 #ifdef __APPLE__
130 //	openRecentMenu(NULL),
131 #endif // __APPLE__
132 	alertHandler(new AlertHandler(this)),
133 #if !defined(_NO_BOOST) || defined(USE_CPP11)
134 	libraryUpdater(NULL),
135 #endif
136 	libraryUpdateProgressReady(false),
137 	libraryUpdateWindow(NULL),
138 	lightingSelection(0),
139 	commandLineSnapshotSave(false)
140 {
141 	int i;
142 
143 	inputHandler = modelViewer->getInputHandler();
144 	LDLModel::setFileCaseCallback(staticFileCaseCallback);
145 	QImage studImage(":/images/images/StudLogo.png");
146 
147 	TREMainModel::setRawStudTextureData(studImage.bits(),
148 #if QT_VERSION < 0x40600
149 			studImage.numBytes());
150 #else
151 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
152 			studImage.byteCount());
153 #else
154 			studImage.sizeInBytes());
155 #endif
156 #endif
157 
158 	for (i = 0; i < MAX_MOUSE_BUTTONS; i++)
159 	{
160 		mouseButtonsDown[i] = false;
161 	}
162 	preferences = new Preferences(parent,this);
163 	extradir = new ExtraDir(parent,this);
164 	snapshotsettings = new SnapshotSettings(parent,this);
165 	jpegoptions = new JpegOptions(parent,this);
166 	preferences->doApply();
167 	setViewMode(Preferences::getViewMode(),
168 				examineLatLong = Preferences::getLatLongMode(),
169 			keepRightSide = Preferences::getKeepRightSideUp());
170 	setFocusPolicy(Qt::StrongFocus);
171 	setupUserAgent();
172 }
173 
~ModelViewerWidget(void)174 ModelViewerWidget::~ModelViewerWidget(void)
175 {
176 	TCObject::release(snapshotTaker);
177 	TCObject::release(modelViewer);
178 	delete preferences;
179 	delete extensionsPanel;
180 	delete errors;
181 	TCObject::release(alertHandler);
182 	alertHandler = NULL;
183 }
184 
setupUserAgent(void)185 void ModelViewerWidget::setupUserAgent(void)
186 {
187 	// If uname below doesn't work, just use the generic "QT" instead.
188 	QString osName = "QT-";
189 	QString userAgent;
190 	// If we can't parse the version out of the AboutPanel, use 3.2.  Note: this
191 	// should be updated in future versions, but the extraction from the
192 	// AboutPanel hopefully won't fail.
193 	QString ldviewVersion = "4.3";
194 	bool foundVersion = false;
195 	QString fullVersion;
196 
197 	int spot;
198 				osName +=
199 #if   defined (Q_OS_LINUX)
200 "Linux"
201 #elif defined (Q_OS_AIX)
202 "AIX"
203 #elif defined (Q_OS_NETBSD)
204 "NetBSD"
205 #elif defined (Q_OS_FREEBSD)
206 "FreeBSD"
207 #elif defined (Q_OS_SOLARIS)
208 "Solaris"
209 #elif defined (Q_OS_WIN32)
210 "Windows"
211 #elif defined (Q_OS_WIN64)
212 "Windows"
213 #elif defined (Q_OS_HPUX)
214 "HP/UX"
215 #elif defined (Q_OS_CYGWIN)
216 "Cygwin"
217 #elif defined (Q_OS_IRIX)
218 "Irix"
219 #elif defined (Q_OS_OSF)
220 "Osf"
221 #else
222 "unknown"
223 #endif
224 ;
225 	// We're going to grab the version label from the about panel, so make sure
226 	// it's created first.
227 	createAboutPanel();
228 	fullVersion = aboutPanel->getText();
229 	// The version will always begin with a number.
230 	if ((spot = fullVersion.indexOf(
231 #if QT_VERSION >= 0x60000
232 		QRegularExpression("[0-9]")
233 #else
234 		QRegExp("[0-9]")
235 #endif
236 		)) != -1)
237 	{
238 		fullVersion = fullVersion.right(fullVersion.length() - spot);
239 		// The first thing after the version is an open parenthesis.  Look
240 		// for that.
241 		if ((spot = fullVersion.indexOf("(")) != -1)
242 		{
243 			fullVersion = fullVersion.left(spot);
244 			ldviewVersion = fullVersion.trimmed();
245 			foundVersion = true;
246 		}
247 	}
248 	// Even though we have a default value for the version, we REALLY want to
249 	// extract it from the about panel.  Assert if the above extraction wasn't
250 	// successful.
251 	assert(foundVersion);
252 	userAgent=QString("LDView/%1 (%2; ldview@gmail.com; "
253 		"https://github.com/tcobbs/ldview)").arg(ldviewVersion).arg(osName);
254 	TCWebClient::setUserAgent(userAgent.toLatin1().constData());
255 }
256 
setApplication(QApplication * value)257 void ModelViewerWidget::setApplication(QApplication *value)
258 {
259 	char *arg0;
260 
261 	app = value;
262 	arg0 = copyString(QCoreApplication::arguments().at(0).toUtf8().constData());
263 	if (strrchr(arg0, '/'))
264 	{
265 		*strrchr(arg0, '/') = 0;
266 	}
267 	modelViewer->setProgramPath(arg0);
268 	delete arg0;
269 	QString arg1;
270 	if (QCoreApplication::arguments().size()>1 && QString::compare (QCoreApplication::arguments().at(1), "-specialcharacters")==0)
271 	{
272 		QMessageBox::information(this, "Special Characters",
273 			QString::fromWCharArray(TCLocalStrings::get(L"SpecialCharacters")),
274 			QMessageBox::Ok, QMessageBox::NoButton);
275 	}
276     QString fontFilePath = findPackageFile("SansSerif.fnt");
277     QFile fontFile (fontFilePath);
278     if (fontFile.exists())
279     {
280         int len = fontFile.size();
281         if (len > 0)
282         {
283             char *buffer = (char*)malloc(len);
284             if ( fontFile.open( QIODevice::ReadOnly ) )
285             {
286                 QDataStream stream( &fontFile );
287                 stream.readRawData(buffer,len);
288                 modelViewer->setFontData((TCByte*)buffer,len);
289             }
290         }
291     }
292 	QImage fontImage2x(":/images/images/SanSerif@2x.png");
293 #if QT_VERSION < 0x40600
294 	long len = fontImage2x.numBytes();
295 #else
296 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
297 	long len = fontImage2x.byteCount();
298 #else
299 	long len = fontImage2x.sizeInBytes();
300 #endif
301 #endif
302 	modelViewer->setRawFont2xData(fontImage2x.bits(),len);
303 
304 	bool shouldExit = false;
305 	// Let LDSnapshotTaker perform an export if requested, but don't try to use
306 	// it to save snapshots, because that doesn't work.
307 	if (LDSnapshotTaker::doCommandLine(false, true))
308 	{
309 		shouldExit = true;
310 	}
311     const TCStringArray *commandLine = TCUserDefaults::getProcessedCommandLine();
312     const char *commandLineFilename = NULL;
313 
314 	TCUserDefaults::removeValue(HFOV_KEY, false);
315 	TCUserDefaults::removeValue(CAMERA_GLOBE_KEY, false);
316     if (commandLine)
317     {
318         int i;
319         int count = commandLine->getCount();
320         for (i = 0; i < count && !commandLineFilename; i++)
321         {
322             const char *arg = commandLine->stringAtIndex(i);
323 
324             if (arg[0] != '-')
325                 commandLineFilename = arg;
326 			if (stringHasCaseInsensitivePrefix(arg, "-ca"))
327 			{
328 				float value;
329 
330 				if (sscanf(arg + 3, "%f", &value) == 1)
331 				{
332 					TCUserDefaults::setFloatForKey(value, HFOV_KEY, false);
333 				}
334 			}
335 			else if (stringHasCaseInsensitivePrefix(arg, "-cg"))
336 			{
337 				TCUserDefaults::setStringForKey(arg + 3, CAMERA_GLOBE_KEY,false);
338 			}
339         }
340     }
341 	char *snapshotFilename =
342 		TCUserDefaults::stringForKey(SAVE_SNAPSHOT_KEY);
343 	commandLineSnapshotSave = (snapshotFilename ? true : false);
344 	QString current = QDir::currentPath();
345     if (commandLineFilename && verifyLDrawDir())
346     {
347 		QUrl qurl(commandLineFilename);
348 		if (qurl.scheme()=="file")
349 		{
350 			commandLineFilename=copyString(QUrl::fromPercentEncoding(qurl.toLocalFile().toUtf8().constData()).toUtf8().constData());
351 		}
352         QFileInfo fi(commandLineFilename);
353         commandLineFilename = copyString(fi.absoluteFilePath().toUtf8().constData());
354 //      loadModel(commandLineFilename);
355         if (chDirFromFilename(commandLineFilename))
356         {
357             modelViewer->setFilename(commandLineFilename);
358 //            modelViewer->loadModel();
359 			if (modelViewer->loadModel())
360 			{
361         		getFileInfo(commandLineFilename, lastWriteTime, lastFileSize);
362         		if (lastWriteTime.isValid())
363         		{
364             		startPollTimer();
365         		}
366         		setLastOpenFile(commandLineFilename);
367         		mainWindow->populateRecentFileMenuItems();
368 				mainWindow->setupStandardSizes();
369 			    mainWindow->fileSaveSetEnabled(true);
370     			mainWindow->fileReloadSetEnabled(true);
371 				mainWindow->toolbarViewAngleSetEnabled(true);
372 				startPaintTimer();
373 				updateStep();
374     		}
375         }
376     }
377     if (snapshotFilename)
378     {
379 		if (snapshotFilename)
380 		{
381 			LDConsoleAlertHandler *consoleAlertHandler = LDSnapshotTaker::getConsoleAlertHandler();
382 			QDir::setCurrent(current);
383 			QFileInfo fi(snapshotFilename);
384 			QString s(snapshotFilename);
385 			char *s2=copyString(fi.absoluteFilePath().toUtf8().constData());
386 
387 			QString ext = s.toLower().right(4);
388 			if (ext == ".png")
389 			{
390 				saveImageType = PNG_IMAGE_TYPE_INDEX;
391 			}
392 			else if (ext == ".bmp")
393 			{
394 				saveImageType = BMP_IMAGE_TYPE_INDEX;
395 			}
396 			else
397 			{
398 				saveImageType = JPG_IMAGE_TYPE_INDEX;
399 			}
400 			saveImage(s2,
401 				TCUserDefaults::longForKey(SAVE_ACTUAL_SIZE_KEY, 1, false) ?
402 				TCUserDefaults::longForKey(WINDOW_WIDTH_KEY, WIN_WIDTH, false) :
403 				TCUserDefaults::longForKey(SAVE_WIDTH_KEY, 1024, false),
404 				TCUserDefaults::longForKey(SAVE_ACTUAL_SIZE_KEY, 1, false) ?
405 				TCUserDefaults::longForKey(WINDOW_HEIGHT_KEY, WIN_HEIGHT, false) :
406 				TCUserDefaults::longForKey(SAVE_HEIGHT_KEY, 768, false), true);
407 			TCObject::release(consoleAlertHandler);
408 		}
409 		shouldExit = true;
410 	}
411 	if (shouldExit)
412 	{
413 		exit(0);
414 	}
415 }
416 
initializeGL(void)417 void ModelViewerWidget::initializeGL(void)
418 {
419 	lock();
420 	TREGLExtensions::setup();
421 	preferences->doCancel();
422 	doViewStatusBar(preferences->getStatusBar());
423 	doViewToolBar(preferences->getToolBar());
424 	if (saving || printing)
425 	{
426 		modelViewer->setup();
427 		modelViewer->openGlWillEnd();
428 //		modelViewer->recompile();
429 	}
430 	unlock();
431 }
432 
resizeGL(int width,int height)433 void ModelViewerWidget::resizeGL(int width, int height)
434 {
435 	lock();
436 	if (!loading && !saving && !printing)
437 	{
438 		QSize mainWindowSize = mainWindow->size();
439 
440 		modelViewer->setWidth(mwidth=width);
441 		modelViewer->setHeight(mheight=height);
442 		glViewport(0, 0, width, height);
443 		preferences->setWindowSize(mainWindowSize.width(),
444 			mainWindowSize.height());
445 	}
446 	unlock();
447 }
448 
swap_Buffers(void)449 void ModelViewerWidget::swap_Buffers(void)
450 {
451     glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_FASTEST);
452     glDisable(GL_MULTISAMPLE_ARB);
453     glDrawBuffer(GL_FRONT);
454 //    drawFPS();
455     glDrawBuffer(GL_BACK);
456     glFlush();
457     glEnable(GL_MULTISAMPLE_ARB);
458     glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
459 }
460 
paintGL(void)461 void ModelViewerWidget::paintGL(void)
462 {
463 	lock();
464 	if (!isFboActive() && !painting && (saving || printing || !loading))
465 	{
466 		painting = true;
467 		glEnable(GL_DEPTH_TEST);
468 		if (saving || printing)
469 		{
470 			if (!TREGLExtensions::haveFramebufferObjectExtension())
471 			{
472 				glDrawBuffer(GL_BACK);
473 				glReadBuffer(GL_BACK);
474 			}
475 			if (saving) {
476 				saveImageResult = snapshotTaker->saveImage(saveImageFilename,
477 					saveImageWidth, saveImageHeight, saveImageZoomToFit);
478 			}
479 			if (printing) {
480 			}
481 		}
482 		else
483 		{
484 			makeCurrent();
485 			//updateSpinRate();
486 			redrawRequested = false;
487 			modelViewer->update();
488 			//updateFPS();
489 			//if ((fEq(rotationSpeed, 0.0f) && fEq(modelViewer->getZoomSpeed(), 0.0f)
490 			//	&& fEq(modelViewer->getCameraXRotate(), 0.0f)
491 			//	&& fEq(modelViewer->getCameraYRotate(), 0.0f)
492 			//	&& fEq(modelViewer->getCameraZRotate(), 0.0f)
493 			//	&& fEq(modelViewer->getCameraMotion().length(), 0.0f))
494 			//	|| modelViewer->getPaused())
495 			if (!redrawRequested)
496 			{
497 				killPaintTimer();
498 				fps = -1.0f;
499 			}
500 			else
501 			{
502 				startPaintTimer();
503 			}
504 			updateFPS();
505 			updateLatlong();
506 			//swap_Buffers();
507 		}
508 		painting = false;
509 	}
510 	unlock();
511 }
512 
lock(void)513 void ModelViewerWidget::lock(void)
514 {
515 	if (lockCount == 0)
516 	{
517 		//app->lock();
518 	}
519 	lockCount++;
520 }
521 
unlock(void)522 void ModelViewerWidget::unlock(void)
523 {
524 	lockCount--;
525 	if (lockCount == 0)
526 	{
527 		//app->unlock();
528 	}
529 }
530 
setLibraryUpdateProgress(float progress)531 void ModelViewerWidget::setLibraryUpdateProgress(float progress)
532 {
533 	libraryUpdateWindow->setValue((int)(progress * 100));
534 }
535 
timerEvent(QTimerEvent * event)536 void ModelViewerWidget::timerEvent(QTimerEvent* event)
537 {
538 	lock();
539 	if (event->timerId() == paintTimer)
540 	{
541 		if (!loading)
542 		{
543 #if (QT_VERSION >= 0x50400) && defined(QOPENGLWIDGET)
544 			update();
545 #else
546 			updateGL();
547 #endif
548 		}
549 		TCAutoreleasePool::processReleases();
550 	}
551 	else if (event->timerId() == pollTimer)
552 	{
553 		if (!loading)
554 		{
555 			checkFileForUpdates();
556 		}
557 	}
558 	else if (event->timerId() == loadTimer)
559 	{
560 		killLoadTimer();
561 		finishLoadModel();
562 	}
563 	else if (event->timerId() == libraryUpdateTimer)
564 	{
565 		if (libraryUpdateFinishNotified)
566 		{
567 			killTimer(libraryUpdateTimer);
568 			libraryUpdateTimer = 0;
569 			doLibraryUpdateFinished(libraryUpdateFinishCode);
570 		}
571 		else if (!libraryUpdateCanceled)
572 		{
573 			lock();
574 			if (libraryUpdateProgressReady)
575 			{
576 				libraryUpdateProgressReady = false;
577 				libraryUpdateWindow->setLabelText(libraryUpdateProgressMessage);
578 				libraryUpdateProgressMessage = "";
579 				setLibraryUpdateProgress(libraryUpdateProgressValue);
580 			}
581 			unlock();
582 		}
583 	}
584 	unlock();
585 }
586 
paintEvent(QPaintEvent * event)587 void ModelViewerWidget::paintEvent(QPaintEvent *event)
588 {
589 	lock();
590 	if (loading && !saving && !printing)
591 	{
592 		int r, g, b;
593 
594 		preferences->getBackgroundColor(r, g, b);
595 
596 // former Qt bug 79310 caused problem with the next 2 lines
597 
598 		QPainter painter(this);
599 		painter.fillRect(event->rect(), QColor(r, g, b));
600 	}
601 	else if (!saving && !printing)
602 	{
603 #if (QT_VERSION >= 0x50400) && defined(QOPENGLWIDGET)
604 		QOpenGLWidget::paintEvent(event);
605 #else
606 		QGLWidget::paintEvent(event);
607 #endif
608 	}
609 	unlock();
610 }
611 
preLoad(void)612 void ModelViewerWidget::preLoad(void)
613 {
614 	clearErrors();
615 	makeCurrent();
616 	modelViewer->clearBackground();
617 #if (QT_VERSION >= 0x50400) && defined(QOPENGLWIDGET)
618 	update();
619 #else
620 	glDraw();
621 #endif
622 }
623 
postLoad(void)624 void ModelViewerWidget::postLoad(void)
625 {
626 	makeCurrent();
627 	resizeGL(width(), height());
628 	startPaintTimer();
629     mainWindow->fileSaveSetEnabled(true);
630     mainWindow->fileReloadSetEnabled(true);
631 	mainWindow->toolbarViewAngleSetEnabled(true);
632 	updateStep();
633 }
634 
doFileReload(void)635 void ModelViewerWidget::doFileReload(void)
636 {
637 	lock();
638 	if (loading)
639 	{
640 		if (app)
641 		{
642 			app->beep();
643 		}
644 		return;
645 	}
646 	preLoad();
647 	modelViewer->reload();
648 	postLoad();
649 	unlock();
650 }
doFilePrint(void)651 void ModelViewerWidget::doFilePrint(void)
652 {
653 	QPrinter *printer;
654 	printer = new QPrinter(QPrinter::HighResolution);
655 //	printer->setOptionEnabled(QPrinter::PrintSelection,false);
656 //	printer->setOptionEnabled(QPrinter::PrintPageRange,false);
657 	printer->setColorMode(QPrinter::Color);
658 //	printer->setFullPage(true);
659 #if QT_VERSION >= 0x40400
660 #if QT_VERSION >= 0x50300
661 	printer->setPageMargins(QMarginsF(
662 		TCUserDefaults::longForKey(LEFT_MARGIN_KEY, 500, false) / 1000.0f,
663 		TCUserDefaults::longForKey(TOP_MARGIN_KEY, 500, false) / 1000.0f,
664 		TCUserDefaults::longForKey(RIGHT_MARGIN_KEY, 500, false) / 1000.0f,
665 		(qreal)(TCUserDefaults::longForKey(BOTTOM_MARGIN_KEY, 500, false) / 1000.0f)),
666 		QPageLayout::Inch);
667 	printer->setPageOrientation((QPageLayout::Orientation)TCUserDefaults::longForKey(ORIENTATION_KEY,0,false));
668 	printer->setPageSize(QPageSize((QPageSize::PageSizeId)TCUserDefaults::longForKey(PAPER_SIZE_KEY,0,false)));
669 #else
670 	printer->setPageMargins(
671 		TCUserDefaults::longForKey(LEFT_MARGIN_KEY, 500, false) / 1000.0f,
672 		TCUserDefaults::longForKey(TOP_MARGIN_KEY, 500, false) / 1000.0f,
673 		TCUserDefaults::longForKey(RIGHT_MARGIN_KEY, 500, false) / 1000.0f,
674 		(qreal)(TCUserDefaults::longForKey(BOTTOM_MARGIN_KEY, 500, false) / 1000.0f),
675 		QPrinter::Inch);
676 	printer->setOrientation((QPrinter::Orientation)TCUserDefaults::longForKey(ORIENTATION_KEY,0,false));
677 	printer->setPaperSize((QPrinter::PaperSize)TCUserDefaults::longForKey(PAPER_SIZE_KEY,0,false));
678 #endif
679 #endif
680 	QPrintDialog *printdialog = new QPrintDialog(printer);
681 	if (printdialog)
682 	{
683 #if QT_VERSION >= 0x60000
684 		printdialog->setOption(QAbstractPrintDialog::PrintToFile);
685 		printdialog->setOption(QAbstractPrintDialog::PrintShowPageSize);
686 #else
687 		printdialog->setEnabledOptions(
688 					QAbstractPrintDialog::PrintToFile
689 #if QT_VERSION >= 0x40400
690 					| QAbstractPrintDialog::PrintShowPageSize
691 #endif
692 );
693 #endif
694 		printdialog->setMinMax(1,1);
695 		if (printdialog->exec() != QDialog::Accepted) return;
696 
697 #if QT_VERSION >= 0x40400
698 		qreal 	*left  = new qreal,
699 			  	*right = new qreal,
700 				*top   = new qreal,
701 				*bottom= new qreal;
702 #if QT_VERSION >= 0x50300
703 		TCUserDefaults::setLongForKey((long)printer->pageLayout().pageSize().id(),PAPER_SIZE_KEY,false);
704 		TCUserDefaults::setLongForKey((long)printer->pageLayout().orientation(), ORIENTATION_KEY, false);
705 		QMarginsF margins = printer->pageLayout().margins(QPageLayout::Inch);
706 		*left = margins.left();
707 		*right= margins.right();
708 		*top  = margins.top();
709 		*bottom=margins.bottom();
710 #else
711 		TCUserDefaults::setLongForKey((long)printer->paperSize(),PAPER_SIZE_KEY,false);
712 		TCUserDefaults::setLongForKey((long)printer->orientation(), ORIENTATION_KEY, false);
713 		printer->getPageMargins(left,top,right,bottom,QPrinter::Inch);
714 #endif
715 
716 		TCUserDefaults::setLongForKey((long)(*left*1000),LEFT_MARGIN_KEY,false);
717 		TCUserDefaults::setLongForKey((long)(*right*1000),RIGHT_MARGIN_KEY,false);
718         TCUserDefaults::setLongForKey((long)(*top*1000),TOP_MARGIN_KEY,false);
719         TCUserDefaults::setLongForKey((long)(*bottom*1000),BOTTOM_MARGIN_KEY,false);
720 #endif
721 		QPainter p;
722 		if (!p.begin(printer))
723 			return;
724 //		QPaintDeviceMetrics metrics (p.device());
725 		int dpix = p.device()->logicalDpiX(),
726 		    dpiy = p.device()->logicalDpiY(),
727 			marginx = (int) (2/2.54)*dpix,
728 			marginy = (int) (2/2.54)*dpiy,
729 			pwidth = p.device()->width()-2*marginx,
730 			pheight = p.device()->height()-2*marginy,
731 			bytesPerLine;
732 		long	y, x;
733 //		printf("%ix%i %ix%i DPI\n",pwidth,pheight,dpix,dpiy);
734 		int r, g, b;
735         preferences->getBackgroundColor(r, g, b);
736 		modelViewer->setBackgroundRGB(255,255,255);
737 		if (dpix != dpiy)
738 			modelViewer->setPixelAspectRatio((float)dpix / dpiy);
739 		saveImageType = BMP_IMAGE_TYPE_INDEX;
740 		TCByte *buffer = grabImage(pwidth,pheight,NULL,true);
741 		QImage *image = new QImage(pwidth,pheight,QImage::Format_RGB32);
742 		bytesPerLine = roundUp(pwidth * 3, 4);
743 		for(y = 0 ; y < pheight; y++)
744 			for(x = 0 ; x < pwidth; x++)
745 			{
746 				image->setPixel(x,pheight-y-1,qRgb(buffer[x*3 + y*bytesPerLine],
747 								buffer[x*3 + y*bytesPerLine + 1],
748 								buffer[x*3 + y*bytesPerLine + 2]));
749 			}
750 		p.drawImage(marginx,marginy,*image);
751 		delete image;
752 		delete buffer;
753 		modelViewer->setBackgroundRGB(r, g, b);
754 		modelViewer->setPixelAspectRatio(1.0f);
755 	}
756 	delete printer;
757 	endLoad();
758 	makeCurrent();
759 }
760 
chDirFromFilename(const char * filename)761 bool ModelViewerWidget::chDirFromFilename(const char *filename)
762 {
763 	const char *fileSpot = strrchr(filename, '/');
764 	bool retValue = false;
765 
766 	if (fileSpot)
767 	{
768 		int len = fileSpot - filename;
769 		char *path = new char[len + 1];
770 
771 		strncpy(path, filename, len);
772 		path[len] = 0;
773 		retValue = QDir::setCurrent(path);
774 		if (retValue)
775 		{
776 			Preferences::setLastOpenPath(path);
777 		}
778 		delete path;
779 	}
780 	return retValue;
781 }
782 
finishLoadModel(void)783 void ModelViewerWidget::finishLoadModel(void)
784 {
785 	char *filename = modelViewer->getFilename();
786 
787 	preLoad();
788 	if (modelViewer->loadModel())
789 	{
790 		getFileInfo(filename, lastWriteTime, lastFileSize);
791 		if (lastWriteTime.isValid())
792 		{
793 			startPollTimer();
794 		}
795 		setLastOpenFile(filename);
796 		mainWindow->populateRecentFileMenuItems();
797 		mainWindow->setupStandardSizes();
798 	}
799 	postLoad();
800 }
801 
loadModel(const char * filename)802 void ModelViewerWidget::loadModel(const char *filename)
803 {
804 	if (chDirFromFilename(filename))
805 	{
806 		killPollTimer();
807 		modelViewer->setFilename(filename);
808 		// I'm getting occasional crashes, so schedule load to happen RSN.
809 		startLoadTimer();
810 	}
811 	else
812 	{
813 		QString message;
814 
815 #if QT_VERSION < QT_VERSION_CHECK(5,5,0)
816 		message.sprintf("The directory containing the file %s could not be found.",
817 			filename);
818 #else
819 		message = QString::asprintf("The directory containing the file %s could not be found.",
820 			filename);
821 #endif
822 		QMessageBox::warning(this, "Can't find directory", message, QMessageBox::Ok,
823 			QMessageBox::NoButton);
824 	}
825 }
826 
doFileOpen(void)827 void ModelViewerWidget::doFileOpen(void)
828 {
829 	lock();
830 	if (loading)
831 	{
832 		if (app)
833 		{
834 			app->beep();
835 		}
836 		return;
837 	}
838 	if (verifyLDrawDir())
839 	{
840 		char *initialDir = Preferences::getLastOpenPath();
841 
842 		QDir::setCurrent(initialDir);
843 		delete initialDir;
844 		QString selectedfile = QFileDialog::getOpenFileName(this,"Choose a Model",".",
845 						"All LDraw Files (*.ldr *.dat *.mpd);;"
846 						"LDraw Models (*.ldr *.dat);;Multi-part Models (*.mpd);;All Files (*)");
847 		 if(!selectedfile.isEmpty())
848 		{
849 			QString filename = selectedfile.replace("\\","/");
850 			QDir dir(filename);
851 			QDir::setCurrent(dir.path().replace("\\","/"));
852 			Preferences::setLastOpenPath(dir.path().replace("\\","/").toUtf8().constData());
853 			loadModel(filename.toUtf8().constData());
854 		}
855 	}
856 	unlock();
857 }
858 
setLastOpenFile(const char * filename)859 void ModelViewerWidget::setLastOpenFile(const char *filename)
860 {
861 	if (mainWindow->recentFiles)
862 	{
863 		int index = mainWindow->recentFiles->indexOfString(filename);
864 
865 		mainWindow->recentFiles->insertString(filename);
866 		if (index >= 0)
867 		{
868 			// Insert before removal.  Since the one being removed could have the same
869 			// pointer value as the string in the array, we could otherwise access a
870 			// pointer after it had been deleted.
871 			mainWindow->recentFiles->removeStringAtIndex(index + 1);
872 		}
873 		mainWindow->recordRecentFiles();
874 	}
875 }
876 
877 // Note: static method.
convertMouseButton(int button)878 LDInputHandler::MouseButton ModelViewerWidget::convertMouseButton(int button)
879 {
880 	switch (button)
881 	{
882 	case 1:
883 		return LDInputHandler::MBLeft;
884 	case 2:
885 		return LDInputHandler::MBRight;
886 	case 3:
887 		return LDInputHandler::MBMiddle;
888 	default:
889 		// Don't even try to handle if the button number it too high.
890 		return LDInputHandler::MBUnknown;
891 	}
892 }
893 
mousePressEvent(QMouseEvent * event)894 void ModelViewerWidget::mousePressEvent(QMouseEvent *event)
895 {
896 	lock();
897 	if (loading)
898 	{
899 		unlock();
900 		return;
901 	}
902 	if (!inputHandler->mouseDown(convertKeyModifiers(event->modifiers()),
903 		convertMouseButton(event->button()),
904 #if QT_VERSION >= 0x60000
905 		event->globalPosition().x(),event->globalPosition().y()
906 #else
907 		event->globalX(),event->globalY()
908 #endif
909 		))
910 	{
911 		event->ignore();
912 	}
913 	unlock();
914 }
915 
mouseReleaseEvent(QMouseEvent * event)916 void ModelViewerWidget::mouseReleaseEvent(QMouseEvent *event)
917 {
918 	lock();
919 	if (loading)
920 	{
921 		unlock();
922 		return;
923 	}
924 	if (!inputHandler->mouseUp(convertKeyModifiers(event->modifiers()),
925 		convertMouseButton(event->button()),
926 #if QT_VERSION >= 0x60000
927 		event->globalPosition().x(),event->globalPosition().y()
928 #else
929 		event->globalX(), event->globalY()
930 #endif
931 		))
932 	{
933 		event->ignore();
934 	}
935 	unlock();
936 }
937 
wheelEvent(QWheelEvent * event)938 void ModelViewerWidget::wheelEvent(QWheelEvent *event)
939 {
940 	lock();
941 	if (loading)
942 	{
943 		unlock();
944 		return;
945 	}
946 	if (!inputHandler->mouseWheel(convertKeyModifiers(event->modifiers()),
947 #if QT_VERSION >= 0x50000
948 		(TCFloat)event->angleDelta().y() * 0.5f))
949 #else
950 		(TCFloat)event->delta() * 0.5f))
951 #endif
952 	{
953 		event->ignore();
954 	}
955 	unlock();
956 }
957 
mouseMoveEvent(QMouseEvent * event)958 void ModelViewerWidget::mouseMoveEvent(QMouseEvent *event)
959 {
960 	lock();
961 	if (loading)
962 	{
963 		unlock();
964 		return;
965 	}
966 	if (!inputHandler->mouseMove(convertKeyModifiers(event->modifiers()),
967 #if QT_VERSION >= 0x60000
968 		event->globalPosition().x(),event->globalPosition().y()
969 #else
970 		event->globalX(), event->globalY()
971 #endif
972 		))
973 	{
974 		event->ignore();
975 	}
976 	unlock();
977 }
978 
showPreferences(void)979 void ModelViewerWidget::showPreferences(void)
980 {
981 	preferences->show();
982 }
983 
showFileExtraDir(void)984 void ModelViewerWidget::showFileExtraDir(void)
985 {
986 	extradir->show();
987 }
988 
doLibraryUpdateFinished(int finishType)989 void ModelViewerWidget::doLibraryUpdateFinished(int finishType)
990 {
991 #if !defined(_NO_BOOST) || defined(USE_CPP11)
992     if (libraryUpdater)
993     {
994 		QString statusText;
995 
996 		libraryUpdateWindow->setCancelButtonText(QString::fromWCharArray(TCLocalStrings::get(L"OK")));
997 		setLibraryUpdateProgress(1.0f);
998         if (libraryUpdater->getError() && ucstrlen(libraryUpdater->getError()))
999         {
1000 			QString qError;
1001 
1002 			statusText = QString::fromWCharArray(TCLocalStrings::get(L"LibraryUpdateError"));
1003 			statusText += ":\n";
1004 			ucstringtoqstring(qError, libraryUpdater->getError());
1005 			statusText += qError;
1006         }
1007         switch (finishType)
1008         {
1009         case LIBRARY_UPDATE_FINISHED:
1010             libraryUpdateFinished = true;
1011 			statusText = QString::fromWCharArray(TCLocalStrings::get(L"LibraryUpdateComplete"));
1012             break;
1013         case LIBRARY_UPDATE_CANCELED:
1014             statusText = QString::fromWCharArray(TCLocalStrings::get(L"LibraryUpdateCanceled"));
1015             break;
1016         case LIBRARY_UPDATE_NONE:
1017             statusText = QString::fromWCharArray(TCLocalStrings::get(L"LibraryUpdateUnnecessary"));
1018 			break;
1019         }
1020 		debugPrintf("About to release library updater.\n");
1021         libraryUpdater->release();
1022 		debugPrintf("Released library updater.\n");
1023         libraryUpdater = NULL;
1024         if (statusText.length())
1025         {
1026 			libraryUpdateWindow->setLabelText(statusText);
1027 		}
1028 	}
1029 #endif // _NO_BOOST
1030 }
1031 
showLibraryUpdateWindow(bool initialInstall)1032 void ModelViewerWidget::showLibraryUpdateWindow(bool initialInstall)
1033 {
1034 #if !defined(_NO_BOOST) || defined(USE_CPP11)
1035 	if (!libraryUpdateWindow)
1036 	{
1037 		createLibraryUpdateWindow();
1038 	}
1039 	libraryUpdateWindow->setCancelButtonText(QString::fromWCharArray(TCLocalStrings::get(L"Cancel")));
1040 	libraryUpdateWindow->reset();
1041 	libraryUpdateWindow->show();
1042 	if (initialInstall)
1043 	{
1044 		libraryUpdateWindow->setModal(true);
1045 	}
1046 	else
1047 	{
1048 		libraryUpdateWindow->setModal(false);
1049 		connect(libraryUpdateWindow, SIGNAL(canceled()), this,
1050 			SLOT(doLibraryUpdateCanceled()));
1051 	}
1052 #endif // _NO_BOOST
1053 }
1054 
createLibraryUpdateWindow(void)1055 void ModelViewerWidget::createLibraryUpdateWindow(void)
1056 {
1057 	if (!libraryUpdateWindow)
1058 	{
1059 		libraryUpdateWindow = new QProgressDialog(
1060 						QString::fromWCharArray(TCLocalStrings::get(L"CheckingForUpdates")),
1061 						QString::fromWCharArray(TCLocalStrings::get(L"Cancel")),
1062 						0,100,mainWindow);
1063 		libraryUpdateWindow->setMinimumDuration(0);
1064 		libraryUpdateWindow->setAutoReset(false);
1065 	}
1066 }
1067 
installLDraw(void)1068 bool ModelViewerWidget::installLDraw(void)
1069 {
1070 #if !defined(_NO_BOOST) || defined(USE_CPP11)
1071 	// Don't lock here unless you're REALLY careful.  In particular, you
1072 	// DEFINITELY have to unlock prior to doing the event processing.
1073     if (libraryUpdater)
1074     {
1075         return false;
1076     }
1077     else
1078     {
1079         char *ldrawParentDir = getLDrawDir();
1080         char *ldrawDir = copyString(ldrawParentDir, 255);
1081         QDir originalDir = QDir::current();
1082 		bool progressDialogClosed = false;
1083 
1084         libraryUpdateFinished = false;
1085         strcat(ldrawDir, "/ldraw");
1086 
1087 		QDir dir(ldrawDir);
1088 		if (!dir.exists())
1089 		{
1090 			dir.mkdir(ldrawDir);
1091 		}
1092 		libraryUpdater = new LDLibraryUpdater;
1093         libraryUpdateCanceled = false;
1094 		libraryUpdateFinishNotified = false;
1095 		libraryUpdateFinished = false;
1096 		progressDialogClosed = false;
1097         libraryUpdater->setLibraryUpdateKey(LAST_LIBRARY_UPDATE_KEY);
1098         libraryUpdater->setLdrawDir(ldrawDir);
1099         libraryUpdater->installLDraw();
1100         showLibraryUpdateWindow(true);
1101 		if (!libraryUpdateTimer)
1102 		{
1103 			libraryUpdateTimer = startTimer(50);
1104 		}
1105 		while (libraryUpdater || !progressDialogClosed)
1106 		{
1107 			// We want the update window to be modal, so process events in a
1108 			// tight modal loop.  (See modal section in QProgressDialog
1109 			// documentation.)
1110 			qApp->processEvents();
1111 			if (!progressDialogClosed && libraryUpdateWindow->wasCanceled())
1112 			{
1113 				progressDialogClosed = true;
1114 				// When the install finishes for real, we change the button
1115 				// title from "Cancel" to "OK".  However, it still acts like
1116 				// a cancel.  So check to se if the update really finished, and
1117 				// if it didn't, then note that the user canceled.
1118 				if (!libraryUpdateFinished)
1119 				{
1120 					libraryUpdateCanceled = true;
1121 					doLibraryUpdateFinished(LIBRARY_UPDATE_CANCELED);
1122 					//libraryUpdateCanceled = false;
1123 				}
1124 				break;
1125 			}
1126 			if (libraryUpdateFinishNotified)
1127 			{
1128                 doLibraryUpdateFinished(libraryUpdateFinishCode);
1129 			}
1130 			// Sleep for 50ms.  Unlike the QProgressDialog example, we aren't
1131 			// doing anything inside this loop other than processing the
1132 			// events.  All the work is happening in other threads.  So sleep
1133 			// for a short time in order to avoid monopolizing the CPU.  Keep in
1134 			// mind that while 50ms is essentially unnoticable to a user, it's
1135 			// quite a long time to the computer.
1136 #ifdef WIN32
1137 			Sleep(50);
1138 #else // WIN32
1139 			usleep(50000);
1140 #endif // WIN32
1141 		}
1142         if (libraryUpdateFinished)
1143         {
1144             LDLModel::setLDrawDir(ldrawDir);
1145 			preferences->setLDrawDir(ldrawDir);
1146         }
1147         delete ldrawDir;
1148         return libraryUpdateFinished;
1149 	}
1150 #endif // _NO_BOOST
1151 }
1152 
checkForLibraryUpdates(void)1153 void ModelViewerWidget::checkForLibraryUpdates(void)
1154 {
1155 #if !defined(_NO_BOOST) || defined(USE_CPP11)
1156     if (libraryUpdater)
1157     {
1158         showLibraryUpdateWindow(false);
1159     }
1160     else
1161     {
1162         libraryUpdater = new LDLibraryUpdater;
1163         char *ldrawDir = getLDrawDir();
1164 		wchar_t *updateCheckError = NULL;
1165 
1166         libraryUpdateCanceled = false;
1167 		libraryUpdateFinishNotified = false;
1168 		libraryUpdateFinished = false;
1169         libraryUpdater->setLibraryUpdateKey(LAST_LIBRARY_UPDATE_KEY);
1170         libraryUpdater->setLdrawDir(ldrawDir);
1171         delete ldrawDir;
1172 		if (libraryUpdater->canCheckForUpdates(updateCheckError))
1173 		{
1174 			showLibraryUpdateWindow(false);
1175 			if (!libraryUpdateTimer)
1176 			{
1177 				libraryUpdateTimer = startTimer(50);
1178 			}
1179 			libraryUpdater->checkForUpdates();
1180 		}
1181 		else
1182 		{
1183 			QString qs;
1184 			wcstoqstring(qs, updateCheckError);
1185 			QMessageBox::warning(this,"LDView", qs,
1186 				QMessageBox::Ok, QMessageBox::NoButton);
1187 			delete updateCheckError;
1188 		}
1189     }
1190 #endif // _NO_BOOST
1191 }
1192 
setMainWindow(LDViewMainWindow * value)1193 void ModelViewerWidget::setMainWindow(LDViewMainWindow *value)
1194 {
1195 	int width, height;
1196 
1197 	lock();
1198 	mainWindow = value;
1199 	width = preferences->getWindowWidth();
1200 	height = preferences->getWindowHeight();
1201 	mainWindow->resize(width, height);
1202 /*
1203 	windowSize = mainWindow->size();
1204 	mainWindow->resize(width - 320 + windowSize.width(),
1205 		height - 240 + windowSize.height());
1206 */
1207 	statusBar = mainWindow->statusBar();
1208 //	toolBar = new QToolBar;
1209 	reflectSettings();
1210     mainWindow->fileSaveSetEnabled(false);
1211     mainWindow->fileReloadSetEnabled(false);
1212 	progressBar = new QProgressBar(statusBar);
1213 	progressLabel = new QLabel(statusBar);
1214 	progressLatlong = new QLabel(statusBar);
1215 	progressMode = new QLabel(statusBar);
1216 	progressBar->setTextVisible(false);
1217 	statusBar->addWidget(progressBar);
1218 	statusBar->addWidget(progressLabel, 1);
1219 	statusBar->addWidget(progressLatlong);
1220 	statusBar->addWidget(progressMode);
1221 	mainWindow->setStatusbarOn(preferences->getStatusBar());
1222 	mainWindow->setToolbarOn(preferences->getToolBar());
1223 	mainWindow->setPollAction(Preferences::getPollMode());
1224 	if (viewMode == LDInputHandler::VMExamine)
1225 	{
1226 		mainWindow->setExamineModeOn(true);
1227 		progressMode->setText(QString::fromWCharArray(TCLocalStrings::get(L"ExamineMode")));
1228 	}
1229 	else if (viewMode == LDInputHandler::VMFlyThrough)
1230 	{
1231 		mainWindow->setFlythroughModeOn(true);
1232 		progressMode->setText(QString::fromWCharArray(TCLocalStrings::get(L"FlyThroughMode")));
1233 	}
1234 	else if (viewMode == LDInputHandler::VMWalk)
1235 	{
1236 		mainWindow->setWalkModeOn(true);
1237 		progressMode->setText(QString::fromWCharArray(TCLocalStrings::get(L"WalkMode")));
1238 	}
1239 	mainWindow->setViewLatitudeRotationOn(Preferences::getLatLongMode());
1240 	mainWindow->setKeepRightSideUpOn(keepRightSide = Preferences::getKeepRightSideUp());
1241 	mainWindow->setShowPovAspectRatioOn(
1242 			Preferences::getPovAspectRatio());
1243     saveAlpha = TCUserDefaults::longForKey(SAVE_ALPHA_KEY, 0, false) != 0;
1244 	updateStep();
1245 	unlock();
1246 }
1247 
doRecentFile(int index)1248 void ModelViewerWidget::doRecentFile(int index)
1249 {
1250 	lock();
1251 	if (loading)
1252 	{
1253 		if (app)
1254 		{
1255 			app->beep();
1256 		}
1257 		return;
1258 	}
1259 	if (verifyLDrawDir())
1260 	{
1261 		char *filename = mainWindow->recentFiles->stringAtIndex(index);
1262 
1263 		if (filename)
1264 		{
1265 			QFile file (filename);
1266 			if(!file.exists())
1267 			{
1268 				QString message;
1269 				message = QString::fromWCharArray(TCLocalStrings::get(L"ErrorLoadingModel"));
1270 				message.replace(QString("%s"),QString(filename));
1271 				QMessageBox::warning(this, "LDView", message,
1272 					QMessageBox::Ok, QMessageBox::NoButton);
1273 
1274 			}
1275 			else
1276 				loadModel(filename);
1277 		}
1278 	}
1279 	unlock();
1280 }
1281 
setupProgress(void)1282 void ModelViewerWidget::setupProgress(void)
1283 {
1284 	clearErrors();
1285 	lastProgressTime.start();
1286 	loading = true;
1287 	cancelLoad = false;
1288 }
1289 
endLoad(void)1290 void ModelViewerWidget::endLoad(void)
1291 {
1292 	progressBar->setValue(0);
1293 	progressLabel->setText("");
1294 	loading = false;
1295 }
1296 
progressCallback(const QString & message,float progress,bool)1297 int ModelViewerWidget::progressCallback(const QString &message, float progress,
1298 	bool /*showErrors*/)
1299 {
1300 	if (progress == 2.0f)
1301 	{
1302 		showErrorsIfNeeded(true);
1303 		endLoad();
1304 		makeCurrent();
1305 		return 1;
1306 	}
1307 	if (!loading)
1308 	{
1309 		setupProgress();
1310 	}
1311 	if (message.length())
1312 	{
1313 		progressLabel->setText(message);
1314 	}
1315 	if (progress >= 0.0f)
1316 	{
1317 		progressBar->setValue((int)(progress * 100));
1318 	}
1319 	if (lastProgressTime.elapsed() >= 100 || progress == 1.0f)
1320 	{
1321 		app->processEvents();
1322 		lastProgressTime.start();
1323 		makeCurrent();
1324 	}
1325 	if (cancelLoad)
1326 	{
1327 		endLoad();
1328 		makeCurrent();
1329 		return 0;
1330 	}
1331 	return 1;
1332 }
1333 
doViewStatusBar(bool flag)1334 void ModelViewerWidget::doViewStatusBar(bool flag)
1335 {
1336 	lock();
1337 	if (flag)
1338 	{
1339 		if (statusBar) statusBar->show();
1340 	}
1341 	else
1342 	{
1343 		if (statusBar) statusBar->hide();
1344 	}
1345 	preferences->setStatusBar(flag);
1346 	unlock();
1347 	mainWindow->setupStandardSizes();
1348 }
1349 
doViewToolBar(bool flag)1350 void ModelViewerWidget::doViewToolBar(bool flag)
1351 {
1352     lock();
1353 	mainWindow->showToolbar(flag);
1354 	preferences->setToolBar(flag);
1355 	unlock();
1356 	mainWindow->setupStandardSizes();
1357 }
1358 
doViewFullScreen(void)1359 void ModelViewerWidget::doViewFullScreen(void)
1360 {
1361 	static QPoint pos;
1362 	static QSize size;
1363 	if (!fullscreen)
1364 	{
1365 		pos=mainWindow->pos();
1366 		size=mainWindow->size();
1367 		statusBar->hide();
1368 		//mainWindow->setMainGroupBoxMargin( 0 );
1369 		mainWindow->showToolbar(false);
1370 		mainWindow->showMenubar(false);
1371 		mainWindow->showFullScreen();
1372 		fullscreen=1;
1373 	} else
1374 	{
1375         //mainWindow->setMainGroupBoxMargin( 2 );
1376         mainWindow->showNormal();
1377 		mainWindow->resize(size);
1378 		mainWindow->move(pos);
1379         mainWindow->showMenubar(true);
1380         if(preferences->getStatusBar()) {statusBar->show();}
1381 		if(preferences->getToolBar()) {mainWindow->showToolbar(true);}
1382 		fullscreen=0;
1383 	}
1384 }
1385 
doViewReset(void)1386 void ModelViewerWidget::doViewReset(void)
1387 {
1388 	lock();
1389 	if (loading)
1390 	{
1391 		if (app)
1392 		{
1393 			app->beep();
1394 		}
1395 		return;
1396 	}
1397 	rotationSpeed = 0.0f;
1398 	modelViewer->setRotationSpeed(0.0f);
1399 	modelViewer->setZoomSpeed(0.0f);
1400 	modelViewer->resetView();
1401 	startPaintTimer();
1402 	unlock();
1403 }
1404 
switchExamineLatLong(bool b)1405 void ModelViewerWidget::switchExamineLatLong(bool b)
1406 {
1407 	lock();
1408 	if (examineLatLong != b)
1409 	{
1410 		preferences->setLatLongMode (examineLatLong = b);
1411 		setViewMode (viewMode, examineLatLong, keepRightSide);
1412 		if (b && Preferences::getViewMode() == LDInputHandler::VMExamine)
1413 			progressLatlong->setHidden(false);
1414 		else
1415 			progressLatlong->setHidden(true);
1416 	}
1417 	unlock();
1418 }
1419 
keepRightSideUp(bool b)1420 void ModelViewerWidget::keepRightSideUp(bool b)
1421 {
1422 	lock();
1423 	if (keepRightSide != b)
1424 	{
1425 		preferences->setKeepRightSideUp(keepRightSide = b);
1426 		modelViewer->setKeepRightSideUp(b);
1427 	}
1428 	unlock();
1429 }
1430 
doHelpOpenGLDriverInfo(void)1431 void ModelViewerWidget::doHelpOpenGLDriverInfo(void)
1432 {
1433 	lock();
1434 	if (loading)
1435 	{
1436 		if (app)
1437 		{
1438 			app->beep();
1439 		}
1440 		return;
1441 	}
1442 	if (!extensionsPanel)
1443 	{
1444 		QLabel *extensionsCountLabel;
1445 		QString countString;
1446 		int extensionCount;
1447 		UCSTR temp = LDrawModelViewer::getOpenGLDriverInfo(extensionCount);
1448 
1449 		extensionsPanel = new OpenGLExtensions(mainWindow);
1450 		ucstringtoqstring(openGLDriverInfo, temp);
1451 		extensionsPanel->setText(openGLDriverInfo);
1452 		extensionsCountLabel = new QLabel(extensionsPanel->statusBar());
1453 		countString = QString::number(extensionCount);
1454 		countString += QString::fromWCharArray((TCLocalStrings::get(L"OpenGlnExtensionsSuffix")));
1455 		extensionsCountLabel->setText(countString);
1456 		extensionsPanel->statusBar()->addWidget(extensionsCountLabel, 1);
1457 	}
1458 	extensionsPanel->show();
1459 	extensionsPanel->raise();
1460 //	extensionsPanel->setActiveWindow();
1461 	unlock();
1462 }
1463 
doHelpContents(void)1464 void ModelViewerWidget::doHelpContents(void)
1465 {
1466 	if (loading)
1467 	{
1468 		if (app)
1469 		{
1470 			app->beep();
1471 		}
1472 		return;
1473 	}
1474 	QString helpFilename = findPackageFile(TCLocalStrings::get("HelpHtml"));
1475 	QString qUrl = QString("file://") + helpFilename;
1476 #ifdef __APPLE__
1477 	helpFilename += "#MacNotes";
1478 	CFURLRef url = NULL;
1479 	CFStringRef urlString;
1480 	bool macSuccess = false;
1481 
1482 	urlString = CFStringCreateWithCString(NULL, qUrl.toUtf8(),
1483 		kCFStringEncodingUTF8);
1484 	if (urlString && (url = CFURLCreateWithString(NULL, urlString, NULL)) !=
1485 		NULL)
1486 	{
1487 		if (LSOpenCFURLRef(url, NULL) == 0)
1488 		{
1489 			macSuccess = true;
1490 		}
1491 	}
1492 	if (urlString)
1493 	{
1494 		CFRelease(urlString);
1495 	}
1496 	if (url)
1497 	{
1498 		CFRelease(url);
1499 	}
1500 	if (macSuccess)
1501 	{
1502 		return;
1503 	}
1504 	FSRef fsRef;
1505 	Boolean isDirectory;
1506 /*
1507 	if (FSPathMakeRef((const UInt8 *)(const char *)helpFilename, &fsRef,
1508 		&isDirectory) == 0 && !isDirectory)
1509 	{
1510 		if (LSOpenFSRef(&fsRef, NULL) == 0)
1511 		{
1512 			return;
1513 		}
1514 	}
1515 */
1516 #endif // __APPLE__
1517 	QFile file(helpFilename);
1518 	if (!file.exists())
1519 	{
1520 		return;
1521 	}
1522 	if(!helpContents)
1523 	{
1524 		helpContents = new Help(mainWindow);
1525         if ( file.open( QIODevice::ReadOnly ) ) {
1526             QTextStream stream( &file );
1527             helpContents->setText(
1528 				stream.readAll().replace(
1529 #if QT_VERSION >= 0x60000
1530 					QRegularExpression("(BGCOLOR|COLOR|TEXT|LINK)="),
1531 #else
1532 					QRegExp("(BGCOLOR|COLOR|TEXT|LINK)="),
1533 #endif
1534 												"i=") );
1535         }
1536 	}
1537 	if (!QDesktopServices::openUrl(helpFilename))
1538 		helpContents->show();
1539 }
1540 
doHelpAbout(void)1541 void ModelViewerWidget::doHelpAbout(void)
1542 {
1543 	lock();
1544 	if (loading)
1545 	{
1546 		if (app)
1547 		{
1548 			app->beep();
1549 		}
1550 		return;
1551 	}
1552 	createAboutPanel();
1553 	aboutPanel->show();
1554 	unlock();
1555 }
1556 
createAboutPanel(void)1557 void ModelViewerWidget::createAboutPanel(void)
1558 {
1559 	if (!aboutPanel)
1560 	{
1561 		aboutPanel = new About;
1562 		aboutPanel->resize(10, 10);
1563 		QString text = aboutPanel->getText();
1564 		text.replace( "__DATE__",__DATE__);
1565 		aboutPanel->setText(text);
1566 	}
1567 }
1568 
doHelpAboutQt(void)1569 void ModelViewerWidget::doHelpAboutQt(void)
1570 {
1571 	lock();
1572 	QMessageBox::aboutQt(this,"");
1573 	unlock();
1574 }
1575 
doAboutOK(void)1576 void ModelViewerWidget::doAboutOK(void)
1577 {
1578 	lock();
1579 	aboutPanel->hide();
1580 	unlock();
1581 }
1582 
doLibraryUpdateCanceled(void)1583 void ModelViewerWidget::doLibraryUpdateCanceled(void)
1584 {
1585 	libraryUpdateCanceled = true;
1586 }
1587 
doWireframe(bool value)1588 void ModelViewerWidget::doWireframe(bool value)
1589 {
1590 	preferences->setDrawWireframe(value);
1591 	doApply();
1592 }
1593 
doWireframeFog(bool value)1594 void ModelViewerWidget::doWireframeFog(bool value)
1595 {
1596     preferences->setUseWireframeFog(value);
1597     doApply();
1598 }
1599 
doWireframeRemoveHiddenLines(bool value)1600 void ModelViewerWidget::doWireframeRemoveHiddenLines(bool value)
1601 {
1602     preferences->setRemoveHiddenLines(value);
1603     doApply();
1604 }
1605 
doTextureStud(bool value)1606 void ModelViewerWidget::doTextureStud(bool value)
1607 {
1608     preferences->setTextureStud(value);
1609     doApply();
1610 }
1611 
doShowEdgeOnly(bool value)1612 void ModelViewerWidget::doShowEdgeOnly(bool value)
1613 {
1614 	preferences->setEdgeOnly(value);
1615 	doApply();
1616 }
1617 
doConditionalLine(bool value)1618 void ModelViewerWidget::doConditionalLine(bool value)
1619 {
1620     preferences->setConditionalLine(value);
1621     doApply();
1622 }
1623 
doHighQuality(bool value)1624 void ModelViewerWidget::doHighQuality(bool value)
1625 {
1626     preferences->setHighQuality(value);
1627     doApply();
1628 }
1629 
doAlwaysBlack(bool value)1630 void ModelViewerWidget::doAlwaysBlack(bool value)
1631 {
1632     preferences->setAlwaysBlack(value);
1633     doApply();
1634 }
1635 
doRedBackFaces(bool value)1636 void ModelViewerWidget::doRedBackFaces(bool value)
1637 {
1638 	preferences->setRedBackFaces(value);
1639 	doApply();
1640 }
1641 
doGreenFrontFaces(bool value)1642 void ModelViewerWidget::doGreenFrontFaces(bool value)
1643 {
1644 	preferences->setGreenFrontFaces(value);
1645 	doApply();
1646 }
1647 
doBlueNeutralFaces(bool value)1648 void ModelViewerWidget::doBlueNeutralFaces(bool value)
1649 {
1650 	preferences->setBlueNeutralFaces(value);
1651 	doApply();
1652 }
1653 
doEdge(bool value)1654 void ModelViewerWidget::doEdge(bool value)
1655 {
1656     preferences->setShowsHighlightLines(value);
1657     doApply();
1658 }
1659 
doLighting(bool value)1660 void ModelViewerWidget::doLighting(bool value)
1661 {
1662     preferences->setUseLighting(value);
1663     doApply();
1664 }
1665 
doBFC(bool value)1666 void ModelViewerWidget::doBFC(bool value)
1667 {
1668 	preferences->setUseBFC(value);
1669 	doApply();
1670 }
1671 
doAxes(bool value)1672 void ModelViewerWidget::doAxes(bool value)
1673 {
1674     preferences->setShowAxes(value);
1675     doApply();
1676 }
1677 
doPrimitiveSubstitution(bool value)1678 void ModelViewerWidget::doPrimitiveSubstitution(bool value)
1679 {
1680     preferences->setAllowPrimitiveSubstitution(value);
1681 	doApply();
1682 }
1683 
doSeams(bool value)1684 void ModelViewerWidget::doSeams(bool value)
1685 {
1686 	preferences->setUseSeams(value);
1687 	doApply();
1688 }
1689 
reflectSettings(void)1690 void ModelViewerWidget::reflectSettings(void)
1691 {
1692     if (mainWindow && preferences)
1693     {
1694         mainWindow->setToolbarWireframeOn(preferences->getDrawWireframe());
1695 		mainWindow->setToolbarWireframeFogOn(preferences->getUseWireframeFog());
1696 		mainWindow->setToolbarWireframeRemoveHiddenLinesOn(preferences->getRemoveHiddenLines());
1697 		mainWindow->setToolbarTextureStudOn(preferences->getTextureStud());
1698 		mainWindow->setToolbarEdgeShowEdgeOnlyOn(preferences->getEdgeOnly());
1699 		mainWindow->setToolbarEdgeConditionalLineOn(preferences->getConditionalLine());
1700 		mainWindow->setToolbarEdgeHighQualityOn(preferences->getHighQuality());
1701 		mainWindow->setToolbarEdgeAlwaysBlackOn(preferences->getAlwaysBlack());
1702 		mainWindow->setToolbarBfcRedBackFacesOn(preferences->getRedBackFaces());
1703 		mainWindow->setToolbarBfcGreenFrontFacesOn(preferences->getGreenFrontFaces());
1704 		mainWindow->setToolbarBfcBlueNeutralFacesOn(preferences->getBlueNeutralFaces());
1705 		mainWindow->setToolbarEdgeOn(preferences->getShowsHighlightLines());
1706 		mainWindow->setToolbarLightingOn(preferences->getUseLighting());
1707 		mainWindow->setToolbarBFCOn(preferences->getUseBFC());
1708 		mainWindow->setToolbarAxesOn(preferences->getShowAxes());
1709 		mainWindow->setToolbarSeamsOn(preferences->getUseSeams());
1710 		mainWindow->setToolbarPrimitiveSubstitutionOn(preferences->getAllowPrimitiveSubstitution());
1711     }
1712 }
1713 
updateFPS(void)1714 void ModelViewerWidget::updateFPS(void)
1715 {
1716 	numFramesSinceReference++;
1717 	if (fps == -1.0f)
1718 	{
1719 		referenceFrameTime.start();
1720 		numFramesSinceReference = 0;
1721 		fps = 0.0f;
1722 	}
1723 	else
1724 	{
1725 		long elapsed = referenceFrameTime.elapsed();
1726 
1727 		if (elapsed >= 250)
1728 		{
1729 			fps = 1000.0f / (float)elapsed * numFramesSinceReference;
1730 			referenceFrameTime.start();
1731 			numFramesSinceReference = 0;
1732 		}
1733 	}
1734 	drawFPS();
1735 }
1736 
drawFPS(void)1737 void ModelViewerWidget::drawFPS(void)
1738 {
1739 	if (showFPS && modelViewer->getMainTREModel())
1740 	{
1741 		if (statusBar->isHidden())
1742 		{
1743 			modelViewer->drawFPS(fps);
1744 		}
1745 		else
1746 		{
1747 			QString fpsString;
1748 
1749 			if (fps > 0.0f)
1750 			{
1751 #if QT_VERSION < QT_VERSION_CHECK(5,5,0)
1752 				fpsString.sprintf(TCLocalStrings::get("FPSFormat"), fps);
1753 #else
1754 				fpsString = QString::asprintf(TCLocalStrings::get("FPSFormat"), fps);
1755 #endif
1756 			}
1757 			else
1758 			{
1759 				fpsString = QString::fromWCharArray(TCLocalStrings::get(L"FPSSpinPrompt"));
1760 			}
1761 			progressLabel->setText(fpsString);
1762 		}
1763 	}
1764 }
1765 
updateLatlong(void)1766 void ModelViewerWidget::updateLatlong(void)
1767 {
1768 	if (modelViewer &&
1769 		modelViewer->getViewMode() == LDrawModelViewer::VMExamine &&
1770 		modelViewer->getExamineMode() == LDrawModelViewer::EMLatLong)
1771 	{
1772 		int lat = (int)(modelViewer->getExamineLatitude()+.5);
1773 		int lon = (int)(modelViewer->getExamineLongitude()+.5);
1774 		if (lon == -180) lon = 180;
1775 		QString latlongstring;
1776 #if QT_VERSION < QT_VERSION_CHECK(5,5,0)
1777 		latlongstring.sprintf(TCLocalStrings::get("LatLonFormat"),lat,lon);
1778 #else
1779 		latlongstring = QString::asprintf(TCLocalStrings::get("LatLonFormat"),lat,lon);
1780 #endif
1781 		progressLatlong->setText(latlongstring);
1782 	}
1783 	else progressLatlong->setText("");
1784 
1785 }
1786 
startPaintTimer(void)1787 void ModelViewerWidget::startPaintTimer(void)
1788 {
1789 	if (!paintTimer)
1790 	{
1791 		paintTimer = startTimer(0);
1792 	}
1793 }
1794 
killPaintTimer(void)1795 void ModelViewerWidget::killPaintTimer(void)
1796 {
1797 	if (paintTimer)
1798 	{
1799 		killTimer(paintTimer);
1800 		paintTimer = 0;
1801 	}
1802 }
1803 
startPollTimer(bool immediate)1804 void ModelViewerWidget::startPollTimer(bool immediate)
1805 {
1806 	if (!pollTimer && Preferences::getPollMode() != LDVPollNone)
1807 	{
1808 		pollTimer = startTimer(POLL_INTERVAL);
1809 		if (!loading && immediate)
1810 		{
1811 			checkFileForUpdates();
1812 		}
1813 	}
1814 }
1815 
killPollTimer(void)1816 void ModelViewerWidget::killPollTimer(void)
1817 {
1818 	if (pollTimer)
1819 	{
1820 		killTimer(pollTimer);
1821 		pollTimer = 0;
1822 	}
1823 }
1824 
startLoadTimer(void)1825 void ModelViewerWidget::startLoadTimer(void)
1826 {
1827 	if (!loadTimer)
1828 	{
1829 		loadTimer = startTimer(0);
1830 	}
1831 }
1832 
killLoadTimer(void)1833 void ModelViewerWidget::killLoadTimer(void)
1834 {
1835 	if (loadTimer)
1836 	{
1837 		killTimer(loadTimer);
1838 		loadTimer = 0;
1839 	}
1840 }
1841 
doApply(void)1842 void ModelViewerWidget::doApply(void)
1843 {
1844 	lock();
1845 	startPaintTimer();
1846 	unlock();
1847 }
1848 
verifyLDrawDir(char * value)1849 bool ModelViewerWidget::verifyLDrawDir(char *value)
1850 {
1851 	QString currentDir = QDir::currentPath();
1852 	bool found = false;
1853 	char buf[128];
1854 
1855 	if (QDir::setCurrent(value))
1856 	{
1857 		strcpy(buf, "parts");
1858 		if (staticFileCaseCallback(buf) && QDir::current().cd(buf))
1859 		{
1860 			QDir::setCurrent(value);
1861 			strcpy(buf, "p");
1862 			if (staticFileCaseCallback(buf) && QDir::current().cd(buf))
1863 			{
1864 				LDLModel::setLDrawDir(value);
1865 				found = true;
1866 			}
1867 		}
1868 		QDir::setCurrent(currentDir);
1869 	}
1870 	return found;
1871 }
1872 
verifyLDrawDir(bool forceChoose)1873 bool ModelViewerWidget::verifyLDrawDir(bool forceChoose)
1874 {
1875 	char *lDrawDir = getLDrawDir();
1876 	bool found = false;
1877 
1878 	if (!forceChoose &&
1879 		(!TCUserDefaults::longForKey(VERIFY_LDRAW_DIR_KEY, 1, false) ||
1880 		verifyLDrawDir(lDrawDir)))
1881 	{
1882 		delete lDrawDir;
1883 		found = true;
1884 	}
1885 	else
1886 	{
1887 		if (commandLineSnapshotSave) return true;
1888 		bool   ans = true;
1889 		if (!verifyLDrawDir(lDrawDir))
1890 		{
1891 			ans = (QMessageBox::question(this, "LDView",
1892 				QString::fromWCharArray(TCLocalStrings::get(L"LDrawDirExistsPrompt")), QMessageBox::Yes,
1893 				QMessageBox::No | QMessageBox::Default) == QMessageBox::Yes);
1894 		}
1895 		delete lDrawDir;
1896 		if (ans)
1897 		{
1898 			while (!found)
1899 			{
1900 				if (promptForLDrawDir())
1901 				{
1902 					lDrawDir = getLDrawDir();
1903 					if (verifyLDrawDir(lDrawDir))
1904 					{
1905 						found = true;
1906 					}
1907 					else
1908 					{
1909 						QMessageBox::warning(this,
1910 							QString::fromWCharArray(TCLocalStrings::get(L"InvalidDir")),
1911 							QString::fromWCharArray(TCLocalStrings::get(L"LDrawNotInDir")),
1912 							QMessageBox::Ok, QMessageBox::NoButton);
1913 					}
1914 					delete lDrawDir;
1915 				}
1916 				else
1917 				{
1918 					break;
1919 				}
1920 			}
1921 		}
1922 		else
1923 		{
1924 			if (QMessageBox::warning(this,
1925 				"LDView", QString::fromWCharArray(TCLocalStrings::get(L"WillDownloadLDraw")),
1926 				QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok)
1927 			{
1928 				LDLModel::setLDrawDir("/");
1929 				if (promptForLDrawDir(
1930 					QString::fromWCharArray(TCLocalStrings::get(L"LDrawInstallDirPrompt"))))
1931 				{
1932 					if (installLDraw())
1933 					{
1934 						found = true;
1935 					}
1936 				}
1937 			}
1938 		}
1939 	}
1940 	return found;
1941 }
1942 
getLDrawDir(void)1943 char *ModelViewerWidget::getLDrawDir(void)
1944 {
1945 	char *lDrawDir = Preferences::getLDrawDir();
1946 
1947 	if (!lDrawDir)
1948 	{
1949 		lDrawDir = copyString(getenv("LDRAWDIR"));
1950 		if (!lDrawDir)
1951 		{
1952 			lDrawDir = copyString("/usr/local/share/ldraw");
1953 		}
1954 	}
1955 	stripTrailingPathSeparators(lDrawDir);
1956 	return lDrawDir;
1957 }
1958 
promptForLDrawDir(QString prompt)1959 bool ModelViewerWidget::promptForLDrawDir(QString prompt)
1960 {
1961 	char *initialDir = getLDrawDir();
1962 	bool retValue = false;
1963 
1964 	if (prompt.isEmpty())
1965 	{
1966 		prompt = QString::fromWCharArray(TCLocalStrings::get(L"LDrawDirPrompt"));
1967 	}
1968 	QDir::setCurrent(initialDir);
1969 	QString selectedfile=QFileDialog::getExistingDirectory(this,prompt,".");
1970 	if (!selectedfile.isEmpty())
1971 	{
1972 		Preferences::setLDrawDir(selectedfile.toUtf8().constData());
1973 		retValue = true;
1974 	}
1975 	return retValue;
1976 }
1977 
doFileLDrawDir(void)1978 void ModelViewerWidget::doFileLDrawDir(void)
1979 {
1980 	char *oldDir;
1981 
1982 	lock();
1983 	if (loading)
1984 	{
1985 		if (app)
1986 		{
1987 			app->beep();
1988 		}
1989 		return;
1990 	}
1991 	oldDir = getLDrawDir();
1992 	if (!verifyLDrawDir(true))
1993 	{
1994 		if (oldDir)
1995 		{
1996 			Preferences::setLDrawDir(oldDir);
1997 		}
1998 	}
1999 	delete oldDir;
2000 	unlock();
2001 }
2002 
doFileCancelLoad(void)2003 void ModelViewerWidget::doFileCancelLoad(void)
2004 {
2005 	lock();
2006 	cancelLoad = true;
2007 	unlock();
2008 }
2009 
errorCallback(LDLError * error)2010 int ModelViewerWidget::errorCallback(LDLError* error)
2011 {
2012 	if (!errors)
2013 	{
2014 		errors = new LDViewErrors(mainWindow, preferences);
2015 	}
2016 	errors->addError(error);
2017 	return 1;
2018 }
2019 
clearErrors(void)2020 void ModelViewerWidget::clearErrors(void)
2021 {
2022 	if (errors)
2023 	{
2024 		errors->clear();
2025 	}
2026 }
2027 
showErrorsIfNeeded(bool onlyIfNeeded)2028 void ModelViewerWidget::showErrorsIfNeeded(bool onlyIfNeeded)
2029 {
2030 	if (errors)
2031 	{
2032 		int errorCount = errors->populateListView();
2033 
2034 		if (!onlyIfNeeded || (errorCount && preferences->getShowErrors()))
2035 		{
2036 			errors->show();
2037 		}
2038 	}
2039 }
2040 
doViewErrors(void)2041 void ModelViewerWidget::doViewErrors(void)
2042 {
2043 	lock();
2044 	if (loading)
2045 	{
2046 		if (app)
2047 		{
2048 			app->beep();
2049 		}
2050 		return;
2051 	}
2052 	if (!errors)
2053 	{
2054 		errors = new LDViewErrors(mainWindow, preferences);
2055 	}
2056 	showErrorsIfNeeded(false);
2057 	unlock();
2058 }
2059 
getFileTime(const char * filename,QDateTime & value)2060 void ModelViewerWidget::getFileTime(const char *filename, QDateTime &value)
2061 {
2062 	if (filename)
2063 	{
2064 		if (!fileInfo)
2065 		{
2066 			fileInfo = new QFileInfo;
2067 		}
2068 		fileInfo->setFile(filename);
2069 
2070 		value = QDateTime(fileInfo->lastModified());
2071 	}
2072 	else
2073 	{
2074 		value = QDateTime();
2075 	}
2076 }
2077 
getFileInfo(const char * filename,QDateTime & value,qint64 & size)2078 void ModelViewerWidget::getFileInfo(const char *filename, QDateTime &value, qint64 &size)
2079 {
2080 	if (filename)
2081 	{
2082 		if (!fileInfo)
2083 		{
2084 			fileInfo = new QFileInfo;
2085 		}
2086 		fileInfo->setFile(filename);
2087 
2088 		value = QDateTime(fileInfo->lastModified());
2089 		size = fileInfo->size();
2090 	}
2091 	else
2092 	{
2093 		value = QDateTime();
2094 		size = 0;
2095 	}
2096 }
2097 
checkFileForUpdates(void)2098 void ModelViewerWidget::checkFileForUpdates(void)
2099 {
2100 	if (pollTimer && modelViewer)
2101 	{
2102 		char *filename = modelViewer->getFilename();
2103 
2104 		killPollTimer();
2105 		if (filename)
2106 		{
2107 			QDateTime newWriteTime;
2108 			qint64 newFileSize;
2109 
2110 			getFileInfo(filename, newWriteTime, newFileSize);
2111 			if (newWriteTime != lastWriteTime)
2112 			{
2113 				bool update = true;
2114 
2115 				if(newFileSize != lastFileSize)
2116 				{
2117 					startPollTimer();
2118 					lastFileSize = newFileSize;
2119 					return;
2120 				}
2121 				lastFileSize = 0;
2122 				lastWriteTime = QDateTime(newWriteTime);
2123 				if (Preferences::getPollMode() == LDVPollPrompt)
2124 				{
2125 					if (QMessageBox::information(this, QString::fromWCharArray(TCLocalStrings::get(L"PollFileUpdate")),
2126 						QString::fromWCharArray(TCLocalStrings::get(L"PollReloadCheck")),
2127 						QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes)
2128 					{
2129 						update = false;
2130 					}
2131 				}
2132 				if (update)
2133 				{
2134 					doFileReload();
2135 				}
2136 			}
2137 		}
2138 		startPollTimer();
2139 	}
2140 }
2141 
windowActivationChange(bool oldActive)2142 void ModelViewerWidget::windowActivationChange(bool oldActive)
2143 {
2144 	lock();
2145 	if (isActiveWindow())
2146 	{
2147 		startPollTimer(true);
2148 	}
2149 	else
2150 	{
2151 		if (Preferences::getPollMode() != LDVPollBackground)
2152 		{
2153 			killPollTimer();
2154 		}
2155 	}
2156 	unlock();
2157 #if QT_VERSION < 0x50000
2158 	QGLWidget::windowActivationChange(oldActive);
2159 #endif
2160 }
2161 
doPollChanged(LDVPollMode newMode)2162 void ModelViewerWidget::doPollChanged(LDVPollMode newMode)
2163 {
2164 	lock();
2165 	Preferences::setPollMode(newMode);
2166 	killPollTimer();
2167 	startPollTimer(true);
2168 	unlock();
2169 }
2170 
setViewMode(LDInputHandler::ViewMode value,bool examine,bool keep,bool)2171 void ModelViewerWidget::setViewMode(LDInputHandler::ViewMode value,
2172 bool examine, bool keep, bool /*saveSettings*/)
2173 {
2174 	viewMode = value;
2175 	if (viewMode == LDInputHandler::VMExamine)
2176 	{
2177 		LDrawModelViewer::ExamineMode examineMode = ( examine ?
2178 				LDrawModelViewer::EMLatLong : LDrawModelViewer::EMFree );
2179 		inputHandler->setViewMode(LDInputHandler::VMExamine);
2180 		modelViewer->setConstrainZoom(true);
2181 		if (progressMode)
2182 		{
2183 			progressMode->setText(QString::fromWCharArray(TCLocalStrings::get(L"ExamineMode")));
2184 		}
2185 		if (progressLatlong)
2186 		{
2187 			progressLatlong->setHidden(!examine);
2188 		}
2189 		modelViewer->setExamineMode(examineMode);
2190 	}
2191 	else if (viewMode == LDInputHandler::VMFlyThrough)
2192 	{
2193 		inputHandler->setViewMode(LDInputHandler::VMFlyThrough);
2194 		modelViewer->setConstrainZoom(false);
2195 		modelViewer->setKeepRightSideUp(keep);
2196 		if (progressMode)
2197 		{
2198 			progressMode->setText(QString::fromWCharArray(TCLocalStrings::get(L"FlyThroughMode")));
2199 		}
2200 		if (progressLatlong) progressLatlong->setHidden(true);
2201 	}
2202 	else if (viewMode == LDInputHandler::VMWalk)
2203 	{
2204 		inputHandler->setViewMode(LDInputHandler::VMWalk);
2205 		modelViewer->setKeepRightSideUp(true);
2206 		if (progressMode)
2207 		{
2208 			progressMode->setText(QString::fromWCharArray(TCLocalStrings::get(L"WalkMode")));
2209 		}
2210 	}
2211 	Preferences::setViewMode(viewMode);
2212 }
2213 
doViewModeChanged(LDInputHandler::ViewMode newMode)2214 void ModelViewerWidget::doViewModeChanged(LDInputHandler::ViewMode newMode)
2215 {
2216 	lock();
2217 	setViewMode(newMode,examineLatLong, keepRightSide);
2218 	unlock();
2219 }
2220 
doZoomToFit(void)2221 void ModelViewerWidget::doZoomToFit(void)
2222 {
2223     lock();
2224     if (loading)
2225     {
2226         if (app)
2227         {
2228             app->beep();
2229         }
2230         return;
2231     }
2232 	modelViewer->zoomToFit();
2233     startPaintTimer();
2234     unlock();
2235 }
2236 
doRightSideUp(void)2237 void ModelViewerWidget::doRightSideUp(void)
2238 {
2239 	lock();
2240 	if (loading)
2241 	{
2242 		if (app)
2243 		{
2244 			app->beep();
2245 		}
2246 		return;
2247 	}
2248 	modelViewer->rightSideUp();
2249 	startPaintTimer();
2250 	unlock();
2251 }
2252 
doCameraLocation(void)2253 void ModelViewerWidget::doCameraLocation(void)
2254 {
2255 	cameralocation->show();
2256 }
2257 
doRotationCenter(void)2258 void ModelViewerWidget::doRotationCenter(void)
2259 {
2260 	rotationcenter->show();
2261 }
2262 
staticImageProgressCallback(const wchar_t * message,float progress,void * userData)2263 bool ModelViewerWidget::staticImageProgressCallback(const wchar_t* message,
2264 							float progress, void* userData)
2265 {
2266 	QString qs="";
2267 	if (message) wcstoqstring(qs,message);
2268     return ((ModelViewerWidget*)userData)->progressCallback(qs, progress, true);
2269 }
2270 
writeImage(char * filename,int width,int height,TCByte * buffer,const char * formatName,bool saveAlpha)2271 bool ModelViewerWidget::writeImage(char *filename, int width, int height,
2272                              TCByte *buffer, const char *formatName,
2273 							 bool saveAlpha)
2274 {
2275     TCImage *image = new TCImage;
2276     bool retValue;
2277 	char comment[1024];
2278 
2279     if (saveAlpha)
2280     {
2281         image->setDataFormat(TCRgba8);
2282     }
2283     image->setSize(width, height);
2284     image->setLineAlignment(4);
2285     image->setImageData((TCByte*)buffer);
2286     image->setFormatName(formatName);
2287     image->setFlipped(true);
2288 	if (strcasecmp(formatName, "PNG") == 0)
2289 	{
2290 		strcpy(comment, "Software:!:!:LDView");
2291 	}
2292 	else
2293 	{
2294 		strcpy(comment, "Created by LDView");
2295 	}
2296 	image->setComment(comment);
2297 	if (TCUserDefaults::longForKey(AUTO_CROP_KEY, 0, false))
2298 	{
2299 		image->autoCrop((TCByte)modelViewer->getBackgroundR(),
2300 			(TCByte)modelViewer->getBackgroundG(),
2301 			(TCByte)modelViewer->getBackgroundB());
2302 	}
2303     retValue = image->saveFile(filename, staticImageProgressCallback, this);
2304     image->release();
2305     return retValue;
2306 }
2307 
roundUp(int value,int nearest)2308 int ModelViewerWidget::roundUp(int value, int nearest)
2309 {
2310     return (value + nearest - 1) / nearest * nearest;
2311 }
2312 
setupSnapshotBackBuffer(int imageWidth,int imageHeight)2313 void ModelViewerWidget::setupSnapshotBackBuffer(int imageWidth, int imageHeight)
2314 {
2315     modelViewer->setSlowClear(true);
2316     modelViewer->setWidth(imageWidth);
2317     modelViewer->setHeight(imageHeight);
2318     modelViewer->setup();
2319     glReadBuffer(GL_BACK);
2320 }
2321 
grabImage(int & imageWidth,int & imageHeight,bool fromCommandLine)2322 bool ModelViewerWidget::grabImage(
2323 	int &imageWidth,
2324 	int &imageHeight,
2325 	bool fromCommandLine /*= false*/)
2326 {
2327 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
2328 	if (fbo == NULL)
2329 	{
2330 		QOpenGLFramebufferObjectFormat fboFormat;
2331 		GLsizei fboSize = 1024;
2332 		fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
2333 		fbo = new QOpenGLFramebufferObject(fboSize, fboSize, fboFormat);
2334 		saving = true;
2335 		if (fbo->isValid() && fbo->bind())
2336 		{
2337 			snapshotTaker->setUseFBO(false);
2338 			glViewport(0, 0, fboSize, fboSize);
2339 			if (modelViewer->getMainTREModel() == NULL && !modelViewer->getNeedsReload())
2340 			{
2341 				modelViewer->loadModel(true);
2342 			}
2343 			inputHandler->stopRotation();
2344 			bool retValue = snapshotTaker->saveImage(saveImageFilename, imageWidth, imageHeight, saveImageZoomToFit);
2345 			fbo->release();
2346 			delete fbo;
2347 			fbo = NULL;
2348 			saving = false;
2349 			return retValue;
2350 		}
2351 		else
2352 		{
2353 			delete fbo;
2354 			fbo = NULL;
2355 			saving = false;
2356 		}
2357 	}
2358 #endif
2359 	if (!fromCommandLine)
2360 	{
2361 		saving = true;
2362 		makeCurrent();
2363 		if (modelViewer->getMainTREModel() == NULL && !modelViewer->getNeedsReload())
2364 		{
2365 			modelViewer->loadModel(true);
2366 		}
2367 		bool retValue = snapshotTaker->saveImage(saveImageFilename, imageWidth, imageHeight, saveImageZoomToFit);
2368 		saving = false;
2369 		return retValue;
2370 	}
2371     int newWidth = 800;
2372     int newHeight = 600;
2373 	int origWidth = mwidth;
2374 	int origHeight = mheight;
2375     int numXTiles, numYTiles;
2376 	bool origSlowClear = modelViewer->getSlowClear();
2377 	int origMemoryUsage = modelViewer->getMemoryUsage();
2378 
2379 	saving = true;
2380 	modelViewer->setMemoryUsage(0);
2381 	if (snapshotTaker->getUseFBO())
2382 	{
2383 		newWidth = snapshotTaker->getFBOSize();
2384 		newHeight = snapshotTaker->getFBOSize();
2385 	}
2386 	snapshotTaker->calcTiling(imageWidth, imageHeight, newWidth, newHeight,
2387 		numXTiles, numYTiles);
2388 	if (!snapshotTaker->getUseFBO())
2389 	{
2390 		setupSnapshotBackBuffer(newWidth, newHeight);
2391 	}
2392     imageWidth = newWidth * numXTiles;
2393     imageHeight = newHeight * numYTiles;
2394 	saveImageWidth = imageWidth;
2395 	saveImageHeight = imageHeight;
2396 	if (snapshotTaker->getUseFBO())
2397 	{
2398 		makeCurrent();
2399 		if (fromCommandLine)
2400 		{
2401 			saveImageResult = snapshotTaker->saveImage();
2402 		}
2403 		else
2404 		{
2405 			saveImageResult = snapshotTaker->saveImage(saveImageFilename,
2406 				saveImageWidth, saveImageHeight, saveImageZoomToFit);
2407 		}
2408 	}
2409 	else
2410 	{
2411 #if (QT_VERSION >= 0x50400) && defined(QOPENGLWIDGET)
2412 //		Code to be added for renderPixmap functionality
2413 //		Without this code saved snapshot image is corrupted/garbage
2414 #else
2415 		renderPixmap(newWidth, newHeight);
2416 #endif
2417 	}
2418 	makeCurrent();
2419 	TREGLExtensions::setup();
2420 	if (!snapshotTaker->getUseFBO())
2421 	{
2422 		modelViewer->openGlWillEnd();
2423 	}
2424 	saving = false;
2425 	mwidth = origWidth;
2426 	mheight = origHeight;
2427 	modelViewer->setWidth(mwidth);
2428 	modelViewer->setHeight(mheight);
2429 	modelViewer->setMemoryUsage(origMemoryUsage);
2430 	modelViewer->setSlowClear(origSlowClear);
2431 	modelViewer->setup();
2432 	doApply();
2433 	return saveImageResult;
2434 }
2435 
2436 
2437 
grabImage(int & imageWidth,int & imageHeight,TCByte * buffer,bool,bool *)2438 TCByte *ModelViewerWidget::grabImage(
2439 	int &imageWidth,
2440 	int &imageHeight,
2441 	TCByte *buffer, bool /*zoomToFit*/,
2442 	bool * /*saveAlpha*/)
2443 {
2444     int newWidth = 800;
2445     int newHeight = 600;
2446 	int origWidth = mwidth;
2447 	int origHeight = mheight;
2448     int numXTiles, numYTiles;
2449 	bool origSlowClear = modelViewer->getSlowClear();
2450 	int origMemoryUsage = modelViewer->getMemoryUsage();
2451 	bool sa=false;
2452 	TCByte *bufferReturn = NULL;
2453 	if (!snapshotTaker)
2454 	{
2455 		snapshotTaker =  new LDSnapshotTaker(modelViewer);
2456 	}
2457 	if (TREGLExtensions::haveFramebufferObjectExtension())
2458 	{
2459 		snapshotTaker->setUseFBO(true);
2460 	}
2461 	snapshotTaker->setImageType(getSaveImageType());
2462 	snapshotTaker->setTrySaveAlpha(saveAlpha =
2463 		TCUserDefaults::longForKey(SAVE_ALPHA_KEY, 0, false) != 0);
2464 	snapshotTaker->setAutoCrop(
2465 		TCUserDefaults::boolForKey(AUTO_CROP_KEY, false, false));
2466 
2467 	printing = true;
2468 	modelViewer->setMemoryUsage(0);
2469 	if (snapshotTaker->getUseFBO())
2470 	{
2471 		newWidth = snapshotTaker->getFBOSize();
2472 		newHeight = snapshotTaker->getFBOSize();
2473 	}
2474 	snapshotTaker->calcTiling(imageWidth, imageHeight, newWidth, newHeight,
2475 		numXTiles, numYTiles);
2476 	if (!snapshotTaker->getUseFBO())
2477 	{
2478 		setupSnapshotBackBuffer(newWidth, newHeight);
2479 	}
2480     imageWidth = newWidth * numXTiles;
2481     imageHeight = newHeight * numYTiles;
2482 	saveImageWidth = imageWidth;
2483 	saveImageHeight = imageHeight;
2484 	if (snapshotTaker->getUseFBO())
2485 	{
2486 		makeCurrent();
2487 			bufferReturn = snapshotTaker->grabImage(saveImageWidth, saveImageHeight, saveImageZoomToFit,buffer,&sa);
2488 	}
2489 	makeCurrent();
2490 	TREGLExtensions::setup();
2491 	if (!snapshotTaker->getUseFBO())
2492 	{
2493 		modelViewer->openGlWillEnd();
2494 	}
2495 	printing = false;
2496 	mwidth = origWidth;
2497 	mheight = origHeight;
2498 	modelViewer->setWidth(mwidth);
2499 	modelViewer->setHeight(mheight);
2500 	modelViewer->setMemoryUsage(origMemoryUsage);
2501 	modelViewer->setSlowClear(origSlowClear);
2502 	modelViewer->setup();
2503 	doApply();
2504 	return bufferReturn;
2505 }
2506 
calcSaveFilename(char * saveFilename,int)2507 bool ModelViewerWidget::calcSaveFilename(char* saveFilename, int /*len*/)
2508 {
2509 	char* filename = modelViewer->getFilename();
2510 	saveDigits = TCUserDefaults::longForKey(SAVE_DIGITS_KEY, 1, false);
2511 	if (filename)
2512 	{
2513 		char baseFilename[1024];
2514 
2515 		if (strrchr(filename, '/'))
2516 		{
2517 			filename = strrchr(filename, '/') + 1;
2518 		}
2519 		if (strrchr(filename, '\\'))
2520 		{
2521 			filename = strrchr(filename, '\\') + 1;
2522 		}
2523 		strcpy(baseFilename, filename);
2524 		if (strchr(baseFilename, '.'))
2525 		{
2526 			*strchr(baseFilename, '.') = 0;
2527 		}
2528 		if (curSaveOp == LDPreferences::SOExport)
2529 		{
2530 			sprintf(saveFilename, "%s.%s", baseFilename,
2531 				modelViewer->getExporter(
2532 				(LDrawModelViewer::ExportType)exportType)->
2533 				getExtension().c_str());
2534 			return true;
2535 		}
2536 		else
2537 		{
2538 			char format[32] = "%s.%s";
2539 			const char *extension = NULL;
2540 			int max;
2541 			if (TCUserDefaults::longForKey(SAVE_SERIES_KEY, 1, false) != 0)
2542 			{
2543 				max = (int)(pow(10.0, saveDigits + 0.1));
2544 				sprintf(format, "%%s%%0%dd.%%s", saveDigits);
2545 			}
2546 			else max = 2;
2547 			int i;
2548 			for (i = 1; i < max; i++)
2549 			{
2550 				if (saveImageType == PNG_IMAGE_TYPE_INDEX)
2551 				{
2552 					extension = "png";
2553 				}
2554 				else if (saveImageType == BMP_IMAGE_TYPE_INDEX)
2555 				{
2556 					extension = "bmp";
2557 				}
2558 				else if (saveImageType == JPG_IMAGE_TYPE_INDEX)
2559 				{
2560 					extension = "jpg";
2561 				}
2562 				if (TCUserDefaults::longForKey(SAVE_SERIES_KEY, 1, false) != 0)
2563 				{
2564 					sprintf(saveFilename, format, baseFilename, i , extension);
2565 				}
2566 				else
2567 				{
2568 					sprintf(saveFilename, format, baseFilename, extension);
2569 				}
2570 				if (TCUserDefaults::boolForKey(SAVE_STEPS_KEY, false, false))
2571 				{
2572 					QString suffix = TCUserDefaults::stringForKey(SAVE_STEPS_SUFFIX_KEY,
2573 							TCLocalStrings::get("DefaultStepSuffix"), false);
2574 					std::string temp = LDSnapshotTaker::addStepSuffix(saveFilename,
2575                     	    suffix.toUtf8().constData(), 1, modelViewer->getNumSteps());
2576 	                strcpy(saveFilename, temp.c_str());
2577 
2578 				}
2579 				if (!fileExists(saveFilename))
2580 				{
2581 					return true;
2582 				}
2583 			}
2584 			return true;
2585 		}
2586 	}
2587 	return false;
2588 }
2589 
getSaveFilename(char * saveFilename,int len)2590 bool ModelViewerWidget::getSaveFilename(char* saveFilename, int len)
2591 {
2592 	QString initialDir = preferences->getSaveDir(curSaveOp,
2593 		modelViewer->getFilename());
2594 	LDrawModelViewer::ExportType origExportType = modelViewer->getExportType();
2595 	QStringList exportFilters;
2596 //	QStringList::const_iterator exportFilterIt;
2597 
2598 	QDir::setCurrent(initialDir);
2599 	saveImageType = TCUserDefaults::longForKey(SAVE_IMAGE_TYPE_KEY, 1, false);
2600 	exportType = TCUserDefaults::longForKey(SAVE_EXPORT_TYPE_KEY,
2601 		LDrawModelViewer::ETPov, false);
2602 	if (!calcSaveFilename(saveFilename, len))
2603 	{
2604 		saveFilename[0] = 0;
2605 	}
2606 	switch (curSaveOp)
2607 	{
2608 	case LDPreferences::SOExport:
2609 		origExportType = modelViewer->getExportType();
2610 		for (int i = LDrawModelViewer::ETFirst; i <= LDrawModelViewer::ETLast;
2611 			i++)
2612 		{
2613 			const LDExporter *exporter = modelViewer->getExporter(
2614 				(LDrawModelViewer::ExportType)i);
2615 
2616 			if (exporter != NULL)
2617 			{
2618 				ucstring ucFileType = exporter->getTypeDescription();
2619 				QString qFileType;
2620 
2621 				ucstringtoqstring(qFileType, ucFileType);
2622 				qFileType += " (*.";
2623 				qFileType += exporter->getExtension().c_str();
2624 				qFileType += ")";
2625 				exportFilters << qFileType;
2626 			}
2627 		}
2628 		modelViewer->getExporter(origExportType);
2629 		saveDialog = new QFileDialog(this,QString::fromWCharArray(TCLocalStrings::get(L"ExportModel")),".");
2630 		saveDialog->setWindowIcon(QPixmap( ":/images/images/LDViewIcon16.png"));
2631 #if QT_VERSION < 0x40400
2632 		saveDialog->setFilters(exportFilters);
2633 		saveDialog->selectFilter(saveDialog->filters().at(exportType - LDrawModelViewer::ETFirst));
2634 #else
2635 		saveDialog->setNameFilters(exportFilters);
2636 		saveDialog->selectNameFilter(exportFilters.at(exportType - LDrawModelViewer::ETFirst));
2637 #endif
2638 		saveDialog->setFileMode(QFileDialog::AnyFile);
2639 		saveDialog->setAcceptMode(QFileDialog::AcceptSave);
2640 		saveDialog->setLabelText(QFileDialog::Accept,"Export");
2641 
2642 		break;
2643 	case LDPreferences::SOSnapshot:
2644 	default:
2645 		saveDialog = new QFileDialog(this,QString::fromWCharArray(TCLocalStrings::get(L"SaveSnapshot")),".",
2646 			"Portable Network Graphics (*.png);;Windows Bitmap (*.bmp);;Jpeg (*.jpg)");
2647 #if QT_VERSION < 0x40400
2648 		saveDialog->selectFilter(saveDialog->filters().at(saveImageType-1));
2649 #else
2650 		saveDialog->selectNameFilter(saveDialog->nameFilters().at(saveImageType-1));
2651 #endif
2652 		saveDialog->setWindowIcon(QPixmap( ":/images/images/LDViewIcon16.png"));
2653 		saveDialog->setFileMode(QFileDialog::AnyFile);
2654 		saveDialog->setAcceptMode(QFileDialog::AcceptSave);
2655 		saveDialog->setLabelText(QFileDialog::Accept,"Save");
2656 		break;
2657 	}
2658 	saveDialog->selectFile(saveFilename);
2659 	if (saveDialog->exec() == QDialog::Accepted)
2660 	{
2661 		QString selectedfile="";
2662 		if (!saveDialog->selectedFiles().isEmpty())
2663 		{
2664 			selectedfile = saveDialog->selectedFiles()[0];
2665 		}
2666 		QString filename = selectedfile, dir = saveDialog->directory().path();
2667         switch (curSaveOp)
2668         {
2669         case LDPreferences::SOExport:
2670             TCUserDefaults::setPathForKey(dir.toUtf8().constData(), LAST_EXPORT_DIR_KEY, false);
2671             break;
2672         case LDPreferences::SOSnapshot:
2673         default:
2674             TCUserDefaults::setPathForKey(dir.toUtf8().constData(), LAST_SNAPSHOT_DIR_KEY, false);
2675             break;
2676         }
2677 		QDir::setCurrent(dir);
2678 		strncpy(saveFilename,filename.toUtf8().constData(),len);
2679 #if QT_VERSION < 0x40400
2680 		QString filter = saveDialog->selectedFilter();
2681 #else
2682 		QString filter = saveDialog->selectedNameFilter();
2683 #endif
2684 		if (filter.indexOf(".png") != -1)
2685 		{
2686 			saveImageType = PNG_IMAGE_TYPE_INDEX;
2687 		}
2688         if (filter.indexOf(".jpg") != -1)
2689         {
2690             saveImageType = JPG_IMAGE_TYPE_INDEX;
2691         }
2692         if (filter.indexOf(".bmp") != -1)
2693         {
2694             saveImageType = BMP_IMAGE_TYPE_INDEX;
2695         }
2696 		if (filter.indexOf(".pov") != -1)
2697 		{
2698 			exportType = LDrawModelViewer::ETPov;
2699 		}
2700 		if (filter.indexOf(".stl") != -1)
2701 		{
2702 			exportType = LDrawModelViewer::ETStl;
2703 		}
2704 #ifdef EXPORT_3DS
2705 		if (filter.indexOf(".3ds") != -1)
2706 		{
2707 			exportType = LDrawModelViewer::ET3ds;
2708 		}
2709 #endif
2710 
2711 		TCUserDefaults::setLongForKey(saveImageType, SAVE_IMAGE_TYPE_KEY,
2712 			false);
2713 		TCUserDefaults::setLongForKey(exportType, SAVE_EXPORT_TYPE_KEY, false);
2714 		if(strlen(saveFilename)>5 && saveFilename[strlen(saveFilename)-4]!='.')
2715 		{
2716 			if (saveImageType == PNG_IMAGE_TYPE_INDEX)
2717 			{
2718 				strcat(saveFilename, ".png");
2719 			}
2720 			else if (saveImageType == BMP_IMAGE_TYPE_INDEX)
2721 			{
2722 				strcat(saveFilename, ".bmp");
2723 			}
2724 			else if (saveImageType == JPG_IMAGE_TYPE_INDEX)
2725 			{
2726 				strcat(saveFilename, ".jpg");
2727 			}
2728 		}
2729 		delete saveDialog;
2730 		return true;
2731 	}
2732 	delete saveDialog;
2733 	return false;
2734 }
2735 
writeBmp(char * filename,int width,int height,TCByte * buffer)2736 bool ModelViewerWidget::writeBmp(char *filename, int width, int height,
2737 								 TCByte *buffer)
2738 {
2739 	return writeImage(filename, width, height, buffer, "BMP");
2740 }
2741 
writePng(char * filename,int width,int height,TCByte * buffer,bool saveAlpha)2742 bool ModelViewerWidget::writePng(char *filename, int width, int height,
2743 								 TCByte *buffer, bool saveAlpha)
2744 {
2745 	return writeImage(filename, width, height, buffer, "PNG", saveAlpha);
2746 }
2747 
getSaveImageType(void)2748 LDSnapshotTaker::ImageType ModelViewerWidget::getSaveImageType(void)
2749 {
2750 	switch (saveImageType)
2751 	{
2752 		case PNG_IMAGE_TYPE_INDEX:
2753 			return LDSnapshotTaker::ITPng;
2754 		case BMP_IMAGE_TYPE_INDEX:
2755 			return LDSnapshotTaker::ITBmp;
2756 		case JPG_IMAGE_TYPE_INDEX:
2757 			return LDSnapshotTaker::ITJpg;
2758 		default:
2759 			return LDSnapshotTaker::ITPng;
2760 	}
2761 }
2762 
saveImage(char * filename,int imageWidth,int imageHeight,bool fromCommandLine)2763 bool ModelViewerWidget::saveImage(
2764 	char *filename,
2765 	int imageWidth,
2766 	int imageHeight,
2767 	bool fromCommandLine /*= false*/)
2768 {
2769 	bool retValue = false;
2770 
2771 	if (!snapshotTaker)
2772 	{
2773 		if (fromCommandLine)
2774 		{
2775 //
2776 //	The command line snapshot will be blank if LDSnapshotTaker has no argument
2777 //
2778 			snapshotTaker =  new LDSnapshotTaker(modelViewer);
2779 		}
2780 		else
2781 		{
2782 			snapshotTaker =  new LDSnapshotTaker(modelViewer);
2783 		}
2784 	}
2785 	if (TREGLExtensions::haveFramebufferObjectExtension())
2786 	{
2787 		snapshotTaker->setUseFBO(true);
2788 	}
2789 	snapshotTaker->setImageType(getSaveImageType());
2790 	snapshotTaker->setTrySaveAlpha(saveAlpha =
2791 		TCUserDefaults::longForKey(SAVE_ALPHA_KEY, 0, false) != 0);
2792 	snapshotTaker->setAutoCrop(
2793 		TCUserDefaults::boolForKey(AUTO_CROP_KEY, false, false));
2794 	saveImageFilename = filename;
2795 	//snapshotTaker->setProductVersion(
2796 	//	((LDViewWindow *)parentWindow)->getProductVersion());
2797 	saveImageZoomToFit = TCUserDefaults::longForKey(SAVE_ZOOM_TO_FIT_KEY, 1,
2798 		false);
2799 	retValue = grabImage(imageWidth, imageHeight, fromCommandLine);
2800 	return retValue;
2801 }
2802 
fileExists(const char * filename)2803 bool ModelViewerWidget::fileExists(const char* filename)
2804 {
2805 	FILE* file = fopen(filename, "r");
2806 
2807 	if (file)
2808 	{
2809 		fclose(file);
2810 		return true;
2811 	}
2812 	else
2813 	{
2814 		return false;
2815 	}
2816 }
2817 
shouldOverwriteFile(char * filename)2818 bool ModelViewerWidget::shouldOverwriteFile(char* filename)
2819 {
2820 	char buf[256];
2821 
2822 	sprintf(buf, TCLocalStrings::get("OverwritePrompt"),
2823 		filename);
2824 	switch( QMessageBox::warning( this, "LDView",
2825 		buf,
2826 		QMessageBox::Yes | QMessageBox::Default,
2827 		QMessageBox::No | QMessageBox::Escape )) {
2828 	case QMessageBox::Yes:
2829 		return true;
2830 		break;
2831 	}
2832 		return false;
2833 }
2834 
fileExport()2835 void ModelViewerWidget::fileExport()
2836 {
2837 	curSaveOp = LDPreferences::SOExport;
2838 	char saveFilename[1024] = "";
2839 	if (getSaveFilename(saveFilename, 1024)&&
2840 		(!fileExists(saveFilename)||shouldOverwriteFile(saveFilename)))
2841 	{
2842 		modelViewer->setExportType((LDrawModelViewer::ExportType)exportType);
2843 		modelViewer->exportCurModel(saveFilename);
2844 	}
2845 }
2846 
fileExportOption()2847 void ModelViewerWidget::fileExportOption()
2848 {
2849 	LDViewExportOption exportOption(mainWindow,modelViewer);
2850 
2851 	if (exportOption.exec() == QDialog::Rejected)
2852 	{
2853 		modelViewer->getExporter((LDrawModelViewer::ExportType)0, true);
2854 	}
2855 }
2856 
file3DSExportOption()2857 void ModelViewerWidget::file3DSExportOption()
2858 {
2859 #ifdef EXPORT_3DS
2860 	LDViewExportOption dsExportOption(mainWindow,modelViewer,LDrawModelViewer::ET3ds);
2861 	dsExportOption.setWindowTitle("3DS Export Options");
2862 	if (dsExportOption.exec() == QDialog::Rejected)
2863 	{
2864 		modelViewer->getExporter((LDrawModelViewer::ExportType)0, true);
2865 	}
2866 #endif
2867 }
2868 
doFileSave(void)2869 bool ModelViewerWidget::doFileSave(void)
2870 {
2871 	char saveFilename[1024] = "";
2872 
2873 	return doFileSave(saveFilename);
2874 }
2875 
doFileSave(char * saveFilename)2876 bool ModelViewerWidget::doFileSave(char *saveFilename)
2877 {
2878 	curSaveOp = LDPreferences::SOSnapshot;
2879 	if (getSaveFilename(saveFilename, 1024))
2880 	{
2881 		if(fileExists(saveFilename)&&!shouldOverwriteFile(saveFilename))
2882 		{
2883 			return false;
2884 		}
2885 		return saveImage(saveFilename, TCUserDefaults::longForKey(SAVE_ACTUAL_SIZE_KEY, 1, false) ? TCUserDefaults::longForKey(SAVE_WIDTH_KEY, 1024, false) : modelViewer->getWidth(),
2886 				TCUserDefaults::longForKey(SAVE_ACTUAL_SIZE_KEY, 1, false) ? TCUserDefaults::longForKey(SAVE_HEIGHT_KEY, 768, false) :  modelViewer->getHeight());
2887 	}
2888 	else
2889 	{
2890 		return false;
2891 	}
2892 }
2893 
doFileSaveSettings(void)2894 void ModelViewerWidget::doFileSaveSettings(void)
2895 {
2896 	snapshotsettings->show();
2897 }
2898 
doFileJPEGOptions(void)2899 void ModelViewerWidget::doFileJPEGOptions(void)
2900 {
2901 	jpegoptions->show();
2902 }
2903 
doFrontViewAngle(void)2904 void ModelViewerWidget::doFrontViewAngle(void)
2905 {
2906 	lock();
2907 	if (loading)
2908 	{
2909 		if (app)
2910 		{
2911 			app->beep();
2912 		}
2913 		return;
2914 	}
2915 	rotationSpeed = 0.0f;
2916 	modelViewer->setRotationSpeed(0.0f);
2917 	modelViewer->setZoomSpeed(0.0f);
2918 	modelViewer->resetView(LDVAngleFront);
2919 	startPaintTimer();
2920 	unlock();
2921 }
2922 
doBackViewAngle(void)2923 void ModelViewerWidget::doBackViewAngle(void)
2924 {
2925 	lock();
2926 	if (loading)
2927 	{
2928 		if (app)
2929 		{
2930 			app->beep();
2931 		}
2932 		return;
2933 	}
2934 	rotationSpeed = 0.0f;
2935 	modelViewer->setRotationSpeed(0.0f);
2936 	modelViewer->setZoomSpeed(0.0f);
2937 	modelViewer->resetView(LDVAngleBack);
2938 	startPaintTimer();
2939 	unlock();
2940 }
2941 
doLeftViewAngle(void)2942 void ModelViewerWidget::doLeftViewAngle(void)
2943 {
2944 	lock();
2945 	if (loading)
2946 	{
2947 		if (app)
2948 		{
2949 			app->beep();
2950 		}
2951 		return;
2952 	}
2953 	rotationSpeed = 0.0f;
2954 	modelViewer->setRotationSpeed(0.0f);
2955 	modelViewer->setZoomSpeed(0.0f);
2956 	modelViewer->resetView(LDVAngleLeft);
2957 	startPaintTimer();
2958 	unlock();
2959 }
2960 
doRightViewAngle(void)2961 void ModelViewerWidget::doRightViewAngle(void)
2962 {
2963 	lock();
2964 	if (loading)
2965 	{
2966 		if (app)
2967 		{
2968 			app->beep();
2969 		}
2970 		return;
2971 	}
2972 	rotationSpeed = 0.0f;
2973 	modelViewer->setRotationSpeed(0.0f);
2974 	modelViewer->setZoomSpeed(0.0f);
2975 	modelViewer->resetView(LDVAngleRight);
2976 	startPaintTimer();
2977 	unlock();
2978 }
2979 
doTopViewAngle(void)2980 void ModelViewerWidget::doTopViewAngle(void)
2981 {
2982 	lock();
2983 	if (loading)
2984 	{
2985 		if (app)
2986 		{
2987 			app->beep();
2988 		}
2989 		return;
2990 	}
2991 	rotationSpeed = 0.0f;
2992 	modelViewer->setRotationSpeed(0.0f);
2993 	modelViewer->setZoomSpeed(0.0f);
2994 	modelViewer->resetView(LDVAngleTop);
2995 	startPaintTimer();
2996 	unlock();
2997 }
2998 
doBottomViewAngle(void)2999 void ModelViewerWidget::doBottomViewAngle(void)
3000 {
3001 	lock();
3002 	if (loading)
3003 	{
3004 		if (app)
3005 		{
3006 			app->beep();
3007 		}
3008 		return;
3009 	}
3010 	rotationSpeed = 0.0f;
3011 	modelViewer->setRotationSpeed(0.0f);
3012 	modelViewer->setZoomSpeed(0.0f);
3013 	modelViewer->resetView(LDVAngleBottom);
3014 	startPaintTimer();
3015 	unlock();
3016 }
3017 
doLatLongViewAngle(void)3018 void ModelViewerWidget::doLatLongViewAngle(void)
3019 {
3020     lock();
3021     if (loading)
3022     {
3023         if (app)
3024         {
3025             app->beep();
3026         }
3027         return;
3028     }
3029 	latitudelongitude->exec();
3030 	unlock();
3031 }
3032 
doIsoViewAngle(void)3033 void ModelViewerWidget::doIsoViewAngle(void)
3034 {
3035 	lock();
3036 	if (loading)
3037 	{
3038 		if (app)
3039 		{
3040 			app->beep();
3041 		}
3042 		return;
3043 	}
3044 	rotationSpeed = 0.0f;
3045 	modelViewer->setRotationSpeed(0.0f);
3046 	modelViewer->setZoomSpeed(0.0f);
3047 	modelViewer->resetView(LDVAngleIso);
3048 	startPaintTimer();
3049 	unlock();
3050 }
3051 
doSaveDefaultViewAngle(void)3052 void ModelViewerWidget::doSaveDefaultViewAngle(void)
3053 {
3054 	preferences->doSaveDefaultViewAngle();
3055 }
3056 
cleanupFloats(TCFloat * array,int count)3057 void ModelViewerWidget::cleanupFloats(TCFloat *array, int count)
3058 {
3059 	int i;
3060 
3061 	for (i = 0; i < count; i++)
3062 	{
3063 		if (fabs(array[i]) < 1e-6)
3064 		{
3065 			array[i] = 0.0f;
3066 		}
3067 	}
3068 }
3069 
doShowViewInfo(void)3070 void ModelViewerWidget::doShowViewInfo(void)
3071 {
3072 	QString qmessage,qcl;
3073 	if (modelViewer)
3074 	{
3075 		ucstring message, commandLine;
3076 		if (modelViewer->getViewInfo(message, commandLine))
3077 		{
3078 			ucstringtoqstring(qmessage,message);
3079 			ucstringtoqstring(qcl,commandLine);
3080 			if(QMessageBox::information(this,
3081 				QString::fromWCharArray(TCLocalStrings::get(L"ViewInfoTitle")),
3082 				qmessage, QMessageBox::Ok,
3083 				QMessageBox::Cancel)==QMessageBox::Ok)
3084 			{
3085 				QApplication::clipboard()->setText(qcl);
3086 			}
3087 		}
3088 	}
3089 }
3090 
doShowPovCamera(void)3091 void ModelViewerWidget::doShowPovCamera(void)
3092 {
3093 	if (modelViewer)
3094 	{
3095 		UCSTR userMessage;
3096 		char *povCamera;
3097 		modelViewer->getPovCameraInfo(userMessage, povCamera);
3098 		if (userMessage && povCamera)
3099 		{
3100 			QString quserMessage;
3101 
3102 			ucstringtoqstring(quserMessage, userMessage);
3103 			if (QMessageBox::information(this,
3104 				QString::fromWCharArray(TCLocalStrings::get(L"PovCameraTitle")), quserMessage,
3105 				QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok)
3106 			{
3107 				QApplication::clipboard()->setText(QString(povCamera));
3108 			};
3109 		}
3110 		delete userMessage;
3111 		delete povCamera;
3112 	}
3113 }
doShowPovAspectRatio(bool flag)3114 void ModelViewerWidget::doShowPovAspectRatio(bool flag)
3115 {
3116 	modelViewer->setPovCameraAspect(flag, true);
3117 }
3118 
doPartList(LDHtmlInventory * htmlInventory,LDPartsList * partsList,const char * filename)3119 void ModelViewerWidget::doPartList(
3120 	LDHtmlInventory *htmlInventory,
3121 	LDPartsList *partsList,
3122 	const char *filename)
3123 {
3124 	if (htmlInventory->generateHtml(filename, partsList,
3125 		modelViewer->getFilename()))
3126 	{
3127 		if (htmlInventory->isSnapshotNeeded())
3128 		{
3129 			char *snapshotPath = copyString(htmlInventory->getSnapshotPath());
3130 			bool saveZoomToFit = modelViewer->getForceZoomToFit();
3131             bool saveActualSize = TCUserDefaults::longForKey(SAVE_ACTUAL_SIZE_KEY, 1, false);
3132             int saveWidth = TCUserDefaults::longForKey(SAVE_WIDTH_KEY, 1024, false);
3133             int saveHeight = TCUserDefaults::longForKey(SAVE_HEIGHT_KEY, 768, false);
3134             bool origSteps = TCUserDefaults::boolForKey(SAVE_STEPS_KEY, false,
3135                 false);
3136             int origStep = modelViewer->getStep();
3137 
3138             TCUserDefaults::setBoolForKey(false, SAVE_STEPS_KEY, false);
3139             modelViewer->setStep(modelViewer->getNumSteps());
3140 			htmlInventory->prepForSnapshot(modelViewer);
3141 			modelViewer->setForceZoomToFit(true);
3142 			TCUserDefaults::setLongForKey(false, SAVE_ACTUAL_SIZE_KEY, false);
3143 			TCUserDefaults::setLongForKey(800, SAVE_WIDTH_KEY, false);
3144 			TCUserDefaults::setLongForKey(600, SAVE_HEIGHT_KEY, false);
3145 
3146 			// By saying it's from the command line, none of the above settings
3147 			// will be written to TCUserDefaults.  I know it's not really from
3148 			// the command line, but it produces the behavior we want.
3149 			saveImageType = PNG_IMAGE_TYPE_INDEX;
3150 			saveImage(snapshotPath, 800, 600);
3151 			delete snapshotPath;
3152 			htmlInventory->restoreAfterSnapshot(modelViewer);
3153 			modelViewer->setForceZoomToFit(saveZoomToFit);
3154 			TCUserDefaults::setLongForKey(saveActualSize, SAVE_ACTUAL_SIZE_KEY, false);
3155             TCUserDefaults::setLongForKey(saveWidth, SAVE_WIDTH_KEY, false);
3156             TCUserDefaults::setLongForKey(saveHeight, SAVE_HEIGHT_KEY, false);
3157             modelViewer->setStep(origStep);
3158             TCUserDefaults::setBoolForKey(origSteps, SAVE_STEPS_KEY, false);
3159 			doApply();
3160 		}
3161 	}
3162 	else
3163 	{
3164 		QMessageBox::warning(this,"LDView",
3165 			QString::fromWCharArray(TCLocalStrings::get(L"PLGenerateError")),
3166 			QMessageBox::Ok, QMessageBox::NoButton);
3167 	}
3168 }
doPartList(void)3169 void ModelViewerWidget::doPartList(void)
3170 {
3171 	if (modelViewer)
3172 	{
3173 		LDPartsList *partsList = modelViewer->getPartsList();
3174 		if (partsList)
3175 		{
3176 			LDHtmlInventory *htmlInventory = new LDHtmlInventory;
3177 			PartList *partlist = new PartList(mainWindow, this, htmlInventory);
3178 			if (partlist->exec() == QDialog::Accepted)
3179 			{
3180 			    QString initialDir = preferences->getSaveDir(LDPreferences::SOPartsList,
3181 				modelViewer->getFilename());
3182 				QDir::setCurrent(initialDir);
3183 
3184 				bool done = false;
3185 				char *cFilename = modelViewer->getFilename();
3186 				QString filename;
3187 
3188 				if (cFilename)
3189 				{
3190 					filename = cFilename;
3191 				}
3192 				else
3193 				{
3194 					consolePrintf("No filename from modelViewer.\n");
3195 				}
3196 				int findSpot = filename.lastIndexOf((
3197 #if QT_VERSION >= 0x60000
3198 						QRegularExpression("/\\")
3199 #else
3200 						QRegExp("/\\")
3201 #endif
3202 					));
3203 				if (findSpot >= 0 && findSpot < (int)filename.length())
3204 					filename=filename.mid(findSpot+1);
3205 				findSpot = filename.lastIndexOf(('.'));
3206 				if (findSpot >= 0 && findSpot < (int)filename.length())
3207                     filename=filename.left(findSpot);
3208 				filename += ".html";
3209 				findSpot = filename.lastIndexOf(('/'));
3210 				if (findSpot >= 0 && findSpot < (int)filename.length())
3211 					filename = filename.mid(findSpot + 1);
3212 				QString startWith = QString(htmlInventory->getLastSavePath()) +
3213 					QString("/") + filename;
3214 				QString filter = QString(TCLocalStrings::get("HtmlFileType")) +
3215 					" (*.html)";
3216 				while (!done)
3217 				{
3218 					QString htmlFilename = QFileDialog::getSaveFileName(this,
3219 						QString::fromWCharArray(TCLocalStrings::get(L"GeneratePartsList")),
3220 						initialDir + "/" + filename,
3221 						filter);
3222 					if (htmlFilename.isEmpty())
3223 					{
3224 						done = true;
3225 					}
3226 					else
3227 					{
3228 						if (fileExists(htmlFilename.toUtf8().constData()))
3229 						{
3230 							QString prompt =
3231 								QString::fromWCharArray(TCLocalStrings::get(L"OverwritePrompt"));
3232 
3233 							prompt.replace("%s", htmlFilename);
3234 							if (QMessageBox::warning(this, "LDView", prompt,
3235 								QMessageBox::Yes, QMessageBox::No) ==
3236 								QMessageBox::No)
3237 							{
3238 								continue;
3239 							}
3240 						}
3241 						doPartList(htmlInventory, partsList,
3242 							htmlFilename.toUtf8().constData());
3243 						done = true;
3244 					}
3245 				}
3246 			}
3247 			htmlInventory->release();
3248 			partsList->release();
3249 		}
3250 	}
3251 }
3252 
doModelTree()3253 void ModelViewerWidget::doModelTree()
3254 {
3255 	modeltree->show();
3256 }
3257 
doBoundingBox()3258 void ModelViewerWidget::doBoundingBox()
3259 {
3260 	boundingbox->show();
3261 }
3262 
doMpdModel()3263 void ModelViewerWidget::doMpdModel()
3264 {
3265 	mpdmodel->show();
3266 }
3267 
3268 // Note: static method
convertKeyCode(int osKey)3269 LDInputHandler::KeyCode ModelViewerWidget::convertKeyCode(int osKey)
3270 {
3271 	if (osKey >= Qt::Key_A && osKey <= Qt::Key_Z)
3272 	{
3273 		return (LDInputHandler::KeyCode)(osKey - Qt::Key_A +
3274 			LDInputHandler::KCA);
3275 	}
3276 	else
3277 	{
3278 		switch (osKey)
3279 		{
3280 		case Qt::Key_Up:
3281 			return LDInputHandler::KCUp;
3282 		case Qt::Key_Down:
3283 			return LDInputHandler::KCDown;
3284 		case Qt::Key_Left:
3285 			return LDInputHandler::KCLeft;
3286 		case Qt::Key_Right:
3287 			return LDInputHandler::KCRight;
3288 		case Qt::Key_Space:
3289 			return LDInputHandler::KCSpace;
3290 		case Qt::Key_PageUp:
3291 			return LDInputHandler::KCPageUp;
3292 		case Qt::Key_PageDown:
3293 			return LDInputHandler::KCPageDown;
3294 		case Qt::Key_Home:
3295 			return LDInputHandler::KCHome;
3296 		case Qt::Key_End:
3297 			return LDInputHandler::KCEnd;
3298 		case Qt::Key_Insert:
3299 			return LDInputHandler::KCInsert;
3300 		case Qt::Key_Delete:
3301 			return LDInputHandler::KCDelete;
3302 		case Qt::Key_Escape:
3303 			return LDInputHandler::KCEscape;
3304 		default:
3305 			return LDInputHandler::KCUnknown;
3306 		}
3307 	}
3308 }
3309 
keyPressEvent(QKeyEvent * event)3310 void ModelViewerWidget::keyPressEvent(QKeyEvent *event)
3311 {
3312 	lock();
3313 	if (inputHandler->keyDown(convertKeyModifiers(event->modifiers()),
3314 		convertKeyCode(event->key())))
3315 	{
3316 		event->accept();
3317 	}
3318 	else
3319 	{
3320 		event->ignore();
3321 	}
3322 	if((event->modifiers() & Qt::AltModifier) &&
3323 		(event->key() >= Qt::Key_0) &&
3324 		(event->key() <= Qt::Key_9) && preferences)
3325 	{
3326 		int i = event->key()-Qt::Key_0;
3327 		preferences->performHotKey(i);
3328 	}
3329 	if(event->key() == Qt::Key_F10 && fullscreen)
3330 	{
3331 		doViewFullScreen();
3332 	}
3333 	unlock();
3334 #if (QT_VERSION >= 0x50400) && defined(QOPENGLWIDGET)
3335 	QOpenGLWidget::keyPressEvent(event);
3336 #else
3337 	QGLWidget::keyPressEvent(event);
3338 #endif
3339 }
3340 
keyReleaseEvent(QKeyEvent * event)3341 void ModelViewerWidget::keyReleaseEvent(QKeyEvent *event)
3342 {
3343 	lock();
3344 	if (inputHandler->keyUp(convertKeyModifiers(event->modifiers()),
3345 		convertKeyCode(event->key())))
3346 	{
3347 		event->accept();
3348 	}
3349 	else
3350 	{
3351 		event->ignore();
3352 	}
3353 	unlock();
3354 #if (QT_VERSION >= 0x50400) && defined(QOPENGLWIDGET)
3355 	QOpenGLWidget::keyReleaseEvent(event);
3356 #else
3357 	QGLWidget::keyReleaseEvent(event);
3358 #endif
3359 }
3360 
ldlErrorCallback(LDLError * error)3361 void ModelViewerWidget::ldlErrorCallback(LDLError *error)
3362 {
3363 	if (error)
3364 	{
3365 		if (!errorCallback(error))
3366 		{
3367 			error->cancelLoad();
3368 		}
3369 	}
3370 }
3371 
modelViewerAlertCallback(TCAlert * alert)3372 void ModelViewerWidget::modelViewerAlertCallback(TCAlert *alert)
3373 {
3374 	if (alert)
3375 	{
3376 		QMessageBox::warning(this,"LDView",alert->getMessage(),
3377 			QMessageBox::Ok, QMessageBox::NoButton);
3378 	}
3379 }
3380 
snapshotTakerAlertCallback(TCAlert * alert)3381 void ModelViewerWidget::snapshotTakerAlertCallback(TCAlert *alert)
3382 {
3383 	if (alert->getSender() == snapshotTaker)
3384 	{
3385 		if (strcmp(alert->getMessage(), "MakeCurrent") == 0)
3386 		{
3387 			if (isFboActive())
3388 			{
3389 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
3390 				if (!fbo->isBound())
3391 				{
3392 					fbo->bind();
3393 				}
3394 #endif
3395 			}
3396 			else
3397 			{
3398 				makeCurrent();
3399 			}
3400 			//glEnable(GL_DEPTH_TEST);
3401 		}
3402 	}
3403 }
3404 
redrawAlertCallback(TCAlert *)3405 void ModelViewerWidget::redrawAlertCallback(TCAlert * /*alert*/)
3406 {
3407 	lock();
3408     startPaintTimer();
3409 	redrawRequested = true;
3410 	unlock();
3411 }
3412 
captureAlertCallback(TCAlert *)3413 void ModelViewerWidget::captureAlertCallback(TCAlert * /*alert*/)
3414 {
3415 }
3416 
releaseAlertCallback(TCAlert *)3417 void ModelViewerWidget::releaseAlertCallback(TCAlert * /*alert*/)
3418 {
3419 }
3420 
lightVectorChangedAlertCallback(TCAlert *)3421 void ModelViewerWidget::lightVectorChangedAlertCallback(TCAlert * /*alert*/)
3422 {
3423 	if (preferences)
3424 	{
3425 		preferences->checkLightVector();
3426 	}
3427 }
3428 
libraryUpdateProgress(TCProgressAlert * alert)3429 void ModelViewerWidget::libraryUpdateProgress(TCProgressAlert *alert)
3430 {
3431 	// NOTE: this gets called from inside one of the library update threads.  It
3432 	// does NOT happen in the app's main thread.
3433 
3434 
3435 	// Are we allowed to update widgets from outside the main thread? NOPE!
3436 	lock();
3437 	//debugPrintf("Updater progress (%s): %f\n", alert->getMessage(),
3438 	//	alert->getProgress());
3439 	libraryUpdateProgressMessage = QString::fromWCharArray(alert->getWMessage());
3440 	libraryUpdateProgressValue = alert->getProgress();
3441 	libraryUpdateProgressReady = true;
3442 	unlock();
3443 	if (alert->getProgress() == 1.0f)
3444 	{
3445 		// Progress of 1.0 means the library updater is done.
3446 		if (alert->getExtraInfo())
3447 		{
3448 			// We can't call doLibraryUpdateFinished directly, because we're
3449 			// executing from the library update thread.  The
3450 			// doLibraryUpdateFinished function waits for the library update
3451 			// thread to complete.  That will never happen if it's executing
3452 			// inside the library update thread.  So we record the finish code,
3453 			// tell ourselves we have done so, and the doLibraryUpdateTimer()
3454 			// slot will take care of the rest when running in non-modal mode,
3455 			// and the modal loop will take care of things when running in
3456 			// modal mode (during initial library install).
3457 			if (strcmp((*(alert->getExtraInfo()))[0], "None") == 0)
3458 			{
3459 				libraryUpdateFinishCode = LIBRARY_UPDATE_NONE;
3460 			}
3461 			else
3462 			{
3463 				libraryUpdateFinishCode = LIBRARY_UPDATE_FINISHED;
3464 			}
3465 		}
3466 		else
3467 		{
3468 			libraryUpdateFinishCode = LIBRARY_UPDATE_CANCELED;
3469 		}
3470 		// Just as a note, while I believe that assignment of an int is an
3471 		// atomic operation (and therefore doesn't require thread checking),
3472 		// I'm not 100% sure of this.  So set the code first, and then once
3473 		// it's definitely set, set the notification.  I really couldn't care
3474 		// less if the notification setting is atomic, since I'm only doing a
3475 		// boolean compare on it.
3476 		libraryUpdateFinishNotified = true;
3477 	}
3478 	else if (alert->getProgress() == 2.0f)
3479 	{
3480 		// Progress of 2.0 means the library updater encountered an
3481 		// error.
3482 		libraryUpdateFinishCode = LIBRARY_UPDATE_ERROR;
3483 		libraryUpdateFinishNotified = true;
3484 	}
3485 	if (libraryUpdateCanceled)
3486 	{
3487 		alert->abort();
3488 	}
3489 }
3490 
progressAlertCallback(TCProgressAlert * alert)3491 void ModelViewerWidget::progressAlertCallback(TCProgressAlert *alert)
3492 {
3493 	if (alert && !saving)
3494 	{
3495 		if (strcmp(alert->getSource(), "LDLibraryUpdater") == 0)
3496 		{
3497 			libraryUpdateProgress(alert);
3498 		}
3499 		else
3500 		{
3501 			bool showErrors = true;
3502 			QString message;
3503 
3504 			if (strcmp(alert->getSource(), "TCImage") == 0)
3505 			{
3506 				showErrors = false;
3507 			}
3508 			wcstoqstring(message, alert->getWMessage());
3509 			if (!progressCallback(message, alert->getProgress(),
3510 				showErrors))
3511 			{
3512 				alert->abort();
3513 			}
3514 		}
3515 	}
3516 }
3517 
staticFileCaseLevel(QDir & dir,char * filename)3518 bool ModelViewerWidget::staticFileCaseLevel(QDir &dir, char *filename)
3519 {
3520 	int i;
3521 	int len = strlen(filename);
3522 	QString wildcard;
3523 	QStringList files;
3524 
3525 	if (!dir.isReadable())
3526 	{
3527 		return false;
3528 	}
3529 	for (i = 0; i < len; i++)
3530 	{
3531 		QChar letter = filename[i];
3532 
3533 		if (letter.isLetter())
3534 		{
3535 			wildcard.append('[');
3536 			wildcard.append(letter.toLower());
3537 			wildcard.append(letter.toUpper());
3538 			wildcard.append(']');
3539 		}
3540 		else
3541 		{
3542 			wildcard.append(letter);
3543 		}
3544 	}
3545 	dir.setNameFilters(QStringList(wildcard));
3546 	files = dir.entryList();
3547 	if (files.count())
3548 	{
3549 		QString file = files[0];
3550 
3551 		if (file.length() == (int)strlen(filename))
3552 		{
3553 			// This should never be false, but just want to be sure.
3554 			strcpy(filename, file.toUtf8().constData());
3555 			return true;
3556 		}
3557 	}
3558 	return false;
3559 }
3560 
staticFileCaseCallback(char * filename)3561 bool ModelViewerWidget::staticFileCaseCallback(char *filename)
3562 {
3563 	char *shortName;
3564 	QDir dir;
3565 	char *firstSlashSpot;
3566 
3567 	dir.setFilter(QDir::AllEntries | QDir::Readable | QDir::Hidden | QDir::System);
3568 	replaceStringCharacter(filename, '\\', '/');
3569 	firstSlashSpot = strchr(filename, '/');
3570 	if (firstSlashSpot)
3571 	{
3572 		char *lastSlashSpot = strrchr(filename, '/');
3573 		int dirLen;
3574 		char *dirName;
3575 
3576 		while (firstSlashSpot != lastSlashSpot)
3577 		{
3578 			char *nextSlashSpot = strchr(firstSlashSpot + 1, '/');
3579 
3580 			dirLen = firstSlashSpot - filename + 1;
3581 			dirName = new char[dirLen + 1];
3582 			*nextSlashSpot = 0;
3583 			strncpy(dirName, filename, dirLen);
3584 			dirName[dirLen] = 0;
3585 			if (dirLen)
3586 			{
3587 				dir.setPath(dirName);
3588 				delete dirName;
3589 				if (!staticFileCaseLevel(dir, firstSlashSpot + 1))
3590 				{
3591 					return false;
3592 				}
3593 			}
3594 			firstSlashSpot = nextSlashSpot;
3595 			*firstSlashSpot = '/';
3596 		}
3597 		dirLen = lastSlashSpot - filename;
3598 		dirName = new char[dirLen + 1];
3599 		strncpy(dirName, filename, dirLen);
3600 		dirName[dirLen] = 0;
3601 		dir.setPath(dirName);
3602 		shortName = lastSlashSpot + 1;
3603 		delete dirName;
3604 	}
3605 	else
3606 	{
3607 		shortName = filename;
3608 	}
3609 	return staticFileCaseLevel(dir, shortName);
3610 }
3611 
canSaveAlpha(void)3612 bool ModelViewerWidget::canSaveAlpha(void)
3613 {
3614 	if (saveAlpha && (saveImageType == PNG_IMAGE_TYPE_INDEX))
3615 	{
3616 		GLint alphaBits;
3617 
3618 		glGetIntegerv(GL_ALPHA_BITS, &alphaBits);
3619 		return alphaBits > 0;
3620 	}
3621 	return false;
3622 }
3623 
renderOffscreenImage(void)3624 void ModelViewerWidget::renderOffscreenImage(void)
3625 {
3626 	modelViewer->update();
3627 	repaint();
3628 //	offscreen = renderPixmap();
3629 	if(canSaveAlpha())
3630 	{
3631 		glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT |            GL_VIEWPORT_BIT);
3632 		glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
3633 		glEnable(GL_DEPTH_TEST);
3634 		glDisable(GL_LIGHTING);
3635 		glDisable(GL_POLYGON_OFFSET_FILL);
3636 		glDepthFunc(GL_GREATER);
3637 		glDepthRange(0.0f, 1.0f);
3638 		glBegin(GL_QUADS);
3639 			treGlVertex3f(0.0f, 0.0f, -1.0f);
3640 			treGlVertex3f((TCFloat)mwidth, 0.0f, -1.0f);
3641 			treGlVertex3f((TCFloat)mwidth, (TCFloat)mheight, -1.0f);
3642 			treGlVertex3f(0.0f, (TCFloat)mheight, -1.0f);
3643 		glEnd();
3644 		glColor4ub(0, 0, 0, 129);
3645 		glBlendFunc(GL_DST_ALPHA, GL_SRC_ALPHA);
3646 		glEnable(GL_BLEND);
3647 		glDisable(GL_DEPTH_TEST);
3648 		glEnable(GL_ALPHA_TEST);
3649 		glAlphaFunc(GL_LESS, 1.0);
3650 		glBegin(GL_QUADS);
3651 			treGlVertex3f(0.0f, 0.0f, -1.0f);
3652 			treGlVertex3f((TCFloat)mwidth, 0.0f, -1.0f);
3653 			treGlVertex3f((TCFloat)mwidth, (TCFloat)mheight, -1.0f);
3654 			treGlVertex3f(0.0f, (TCFloat)mheight, -1.0f);
3655 		glEnd();
3656 		glPopAttrib();
3657 	}
3658 }
3659 
calcTiling(int desiredWidth,int desiredHeight,int & bitmapWidth,int & bitmapHeight,int & numXTiles,int & numYTiles)3660 void ModelViewerWidget::calcTiling(int desiredWidth, int desiredHeight,
3661 							 int &bitmapWidth, int &bitmapHeight,
3662 							 int &numXTiles, int &numYTiles)
3663 {
3664 	if (desiredWidth > bitmapWidth)
3665 	{
3666 		numXTiles = (desiredWidth + bitmapWidth - 1) / bitmapWidth;
3667 	}
3668 	else
3669 	{
3670 		numXTiles = 1;
3671 	}
3672 	bitmapWidth = desiredWidth / numXTiles;
3673 	if (desiredHeight > bitmapHeight)
3674 	{
3675 		numYTiles = (desiredHeight + bitmapHeight - 1) / bitmapHeight;
3676 	}
3677 	else
3678 	{
3679 		numYTiles = 1;
3680 	}
3681 	bitmapHeight = desiredHeight / numYTiles;
3682 }
3683 
userDefaultChangedAlertCallback(TCAlert * alert)3684 void ModelViewerWidget::userDefaultChangedAlertCallback(TCAlert *alert)
3685 {
3686 	if (preferences)
3687 	{
3688 		preferences->userDefaultChangedAlertCallback(alert);
3689 	}
3690 }
3691 
doPreferences(void)3692 void ModelViewerWidget::doPreferences(void)
3693 {
3694 	showPreferences();
3695 }
3696 
findPackageFile(const QString & filename)3697 QString ModelViewerWidget::findPackageFile(const QString &filename)
3698 {
3699 	QString dir = QDir::currentPath();
3700 	QFile file(filename);
3701 	QString retValue;
3702 
3703 //	if (!file.exists())
3704 //		QDir::setCurrent("..");
3705 	if (!file.exists())
3706 		QDir::setCurrent("Textures");
3707 	if (!file.exists())
3708 		QDir::setCurrent("/usr/share/ldview");
3709 	if (!file.exists())
3710 		QDir::setCurrent("/usr/local/share/ldview");
3711 	if (!file.exists())
3712 		QDir::setCurrent("/usr/local/etc");
3713 	if (!file.exists())
3714 		QDir::setCurrent("/usr/local/lib");
3715 	if (!file.exists())
3716 		QDir::setCurrent(QDir( QCoreApplication::applicationDirPath() + "/../share/ldview").absolutePath());
3717 	if (!file.exists())
3718 	{
3719 		const char *argv0 = TCUserDefaults::getArgv0();
3720 
3721 		QDir::setCurrent(dir);
3722 		if (argv0)
3723 		{
3724 			char *path = copyString(argv0, filename.length() + 5);
3725 
3726 			if (strrchr(path, '/'))
3727 			{
3728 				*strrchr(path, '/') = 0;
3729 			}
3730 			QDir::setCurrent(path);
3731 		}
3732 	}
3733 	if (file.exists())
3734 	{
3735 		QString newDir = QDir::currentPath();
3736 		retValue = newDir + "/" + file.fileName();
3737 	}
3738 	QDir::setCurrent(dir);
3739 	return retValue;
3740 }
3741 
3742 // Note: static method.
convertKeyModifiers(Qt::KeyboardModifiers osModifiers)3743 TCULong ModelViewerWidget::convertKeyModifiers(Qt::KeyboardModifiers osModifiers)
3744 {
3745 	TCULong retValue = 0;
3746 	if (osModifiers & Qt::ShiftModifier)
3747 	{
3748 		retValue |= LDInputHandler::MKShift;
3749 	}
3750 	if (osModifiers & Qt::ControlModifier)
3751 	{
3752 		retValue |= LDInputHandler::MKControl;
3753 	}
3754 	return retValue;
3755 }
3756 
nextStep()3757 void ModelViewerWidget::nextStep()
3758 {
3759 	if (modelViewer->getStep()>=modelViewer->getNumSteps())
3760 		return;
3761 	modelViewer->setStep(modelViewer->getStep()+1);
3762 	updateStep();
3763 	doApply();
3764 }
3765 
prevStep()3766 void ModelViewerWidget::prevStep()
3767 {
3768 	if (modelViewer->getStep()<=1)
3769 		return;
3770 	modelViewer->setStep(modelViewer->getStep()-1);
3771 	updateStep();
3772 	doApply();
3773 }
3774 
firstStep()3775 void ModelViewerWidget::firstStep()
3776 {
3777 	modelViewer->setStep(1);
3778     updateStep();
3779     doApply();
3780 }
3781 
lastStep()3782 void ModelViewerWidget::lastStep()
3783 {
3784     modelViewer->setStep(modelViewer->getNumSteps());
3785     updateStep();
3786     doApply();
3787 }
3788 
3789 
updateStep()3790 void ModelViewerWidget::updateStep()
3791 {
3792     int step = modelViewer->getStep();
3793     QString max = QString::number(modelViewer->getNumSteps());
3794 	mainWindow->toolbarFirstStepSetEnabled(step>1);
3795     mainWindow->toolbarPrevStepSetEnabled(step>1);
3796     mainWindow->toolbarNextStepSetEnabled(modelViewer->getNumSteps()>step);
3797 	mainWindow->toolbarLastStepSetEnabled(modelViewer->getNumSteps()>step);
3798 	mainWindow->setStepGotoEnabled(modelViewer->getNumSteps()>0);
3799     mainWindow->toolbarMaxStepSetText(" / "+max);
3800     mainWindow->toolbarCurrentStepSetText(QString::number(step));
3801 }
3802 
gotoStep()3803 void ModelViewerWidget::gotoStep()
3804 {
3805 	bool ok;
3806 	int step =
3807 #if QT_VERSION < 0x40500
3808 			QInputDialog::getInteger(
3809 #else
3810 			QInputDialog::getInt(
3811 #endif
3812 			this,"Step","Go to Step:",
3813 			modelViewer->getStep(), 1, modelViewer->getNumSteps(), 1, &ok );
3814 	if (ok)
3815 	{
3816 		modelViewer->setStep(step);
3817 		updateStep();
3818 		doApply();
3819 	}
3820 }
3821 
3822