1 #include "ModelWindow.h"
2 #include <LDLib/LDrawModelViewer.h>
3 #include <TCFoundation/TCMacros.h>
4 #include "LDVExtensionsSetup.h"
5 #include "LDViewWindow.h"
6 #include "JpegOptionsDialog.h"
7 #include "ExportOptionsDialog.h"
8 #include <TCFoundation/TCAutoreleasePool.h>
9 #include <TCFoundation/TCUserDefaults.h>
10 #include <TCFoundation/TCStringArray.h>
11 #include <TCFoundation/mystring.h>
12 #include <TCFoundation/TCImage.h>
13 #include <TCFoundation/TCAlertManager.h>
14 #include <TCFoundation/TCProgressAlert.h>
15 #include <TCFoundation/TCLocalStrings.h>
16 #include <LDLoader/LDLError.h>
17 #include <LDLoader/LDLModel.h>
18 #include <LDLib/LDLibraryUpdater.h>
19 #include <LDLib/LDHtmlInventory.h>
20 #include "Resource.h"
21 #include <Commctrl.h>
22 #include <LDLib/LDUserDefaultsKeys.h>
23 #include <CUI/CUIWindowResizer.h>
24 #include <CUI/CUIScaler.h>
25 #include <TRE/TREMainModel.h>
26 #include <TRE/TREGLExtensions.h>
27 #include <windowsx.h>
28 
29 #if !defined(USE_CPP11) && !defined(_NO_BOOST)
30 #include <boost/bind.hpp>
31 #endif // !_NO_BOOST
32 
33 #if defined(_MSC_VER) && _MSC_VER >= 1400 && defined(_DEBUG)
34 #define new DEBUG_CLIENTBLOCK
35 #endif // _DEBUG
36 
37 #define DISTANCE_MULT 1.325f
38 
39 #define POLL_INTERVAL 500
40 #define POLL_TIMER 1
41 
42 #define FSAA_UPDATE_TIMER 2
43 
44 #define PNG_IMAGE_TYPE_INDEX 1
45 #define BMP_IMAGE_TYPE_INDEX 2
46 #define JPG_IMAGE_TYPE_INDEX 3
47 #define SVG_IMAGE_TYPE_INDEX 4
48 #define EPS_IMAGE_TYPE_INDEX 5
49 #define PDF_IMAGE_TYPE_INDEX 6
50 
51 #define MAX_SNAPSHOT_WIDTH 10000
52 #define MAX_SNAPSHOT_HEIGHT 10000
53 
54 #ifndef ListView_SetCheckState
55    #define ListView_SetCheckState(hwndLV, i, fCheck) \
56       ListView_SetItemState(hwndLV, i, \
57       INDEXTOSTATEIMAGEMASK((fCheck)+1), LVIS_STATEIMAGEMASK)
58 #endif
59 
60 void debugOut(char *fmt, ...);
61 
ControlInfo(void)62 ControlInfo::ControlInfo(void)
63 {
64 #ifdef _LEAK_DEBUG
65 	strcpy(className, "ControlInfo");
66 #endif
67 }
68 
ErrorInfo(void)69 ErrorInfo::ErrorInfo(void)
70 	:m_typeName(NULL)
71 {
72 #ifdef _LEAK_DEBUG
73 	strcpy(className, "ErrorInfo");
74 #endif
75 }
76 
~ErrorInfo(void)77 ErrorInfo::~ErrorInfo(void)
78 {
79 }
80 
dealloc(void)81 void ErrorInfo::dealloc(void)
82 {
83 	delete m_typeName;
84 	TCObject::dealloc();
85 }
86 
setTypeName(CUCSTR typeName)87 void ErrorInfo::setTypeName(CUCSTR typeName)
88 {
89 	if (typeName != m_typeName)
90 	{
91 		delete[] m_typeName;
92 		m_typeName = copyString(typeName);
93 	}
94 }
95 
ModelWindow(CUIWindow * parentWindow,int x,int y,int width,int height)96 ModelWindow::ModelWindow(
97 	CUIWindow* parentWindow,
98 	int x,
99 	int y,
100 	int width,
101 	int height):
102 CUIOGLWindow(parentWindow, x, y, width, height),
103 modelViewer(new LDrawModelViewer(width, height)),
104 snapshotTaker(NULL),
105 numFramesSinceReference(0),
106 firstFPSPass(true),
107 //rotationSpeed(0.0f),
108 //lButtonDown(false),
109 //rButtonDown(false),
110 //mButtonDown(false),
111 hPrefsWindow(NULL),
112 captureCount(0),
113 redrawCount(0),
114 hProgressWindow(NULL),
115 lastProgressUpdate(0),
116 loading(false),
117 //needsRecompile(false),
118 hErrorWindow(NULL),
119 errors(new LDLErrorArray),
120 errorTreePopulated(false),
121 errorInfos(NULL),
122 prefs(new LDViewPreferences(parentWindow->getHInstance(), modelViewer)),
123 applyingPrefs(false),
124 offscreenActive(false),
125 hPBuffer(NULL),
126 hPBufferDC(NULL),
127 hPBufferGLRC(NULL),
128 hBitmapRenderDC(NULL),
129 hBitmapRenderGLRC(NULL),
130 hRenderBitmap(NULL),
131 hPrintDialog(NULL),
132 hStatusBar(NULL),
133 hProgressBar(NULL),
134 windowShown(false),
135 hCurrentDC(NULL),
136 hCurrentGLRC(NULL),
137 errorWindowResizer(NULL),
138 saveWindowResizer(NULL),
139 savingFromCommandLine(false),
140 skipErrorUpdates(false),
141 releasingMouse(false),
142 saveStepSuffix(NULL),
143 userLoad(false),
144 errorCount(0),
145 warningCount(0)
146 #if defined(USE_CPP11) || !defined(_NO_BOOST)
147 ,remoteListener(true)
148 ,remoteMessageID(0)
149 #endif // !_NO_BOOST
150 {
151 	UCSTR programPath = LDViewPreferences::getLDViewPath();
152 	HRSRC hStudLogoResource = FindResource(NULL,
153 		MAKEINTRESOURCE(IDR_STUDLOGO_PNG), RT_PNGDATA_1X);
154 	HRSRC hFontResource = FindResource(NULL, MAKEINTRESOURCE(IDR_SANS_FONT),
155 		RT_RCDATA);
156 
157 	loadSettings();
158 	if (hStudLogoResource)
159 	{
160 		HGLOBAL hStudLogo = LoadResource(NULL, hStudLogoResource);
161 
162 		if (hStudLogo)
163 		{
164 			TCByte *data = (TCByte *)LockResource(hStudLogo);
165 
166 			if (data)
167 			{
168 				DWORD length = SizeofResource(NULL, hStudLogoResource);
169 
170 				if (length)
171 				{
172 					TREMainModel::setStudTextureData(data, length);
173 				}
174 			}
175 		}
176 	}
177 	if (hFontResource)
178 	{
179 		HGLOBAL hFont = LoadResource(NULL, hFontResource);
180 
181 		if (hFont)
182 		{
183 			TCByte *data = (TCByte *)LockResource(hFont);
184 
185 			if (data)
186 			{
187 				DWORD length = SizeofResource(NULL, hFontResource);
188 
189 				if (length)
190 				{
191 					modelViewer->setFontData(data, length);
192 				}
193 			}
194 		}
195 	}
196 	TCImage *fontImage2x = TCImage::createFromResource(NULL, IDR_SANS_FONT, 4,
197 		true, 2.0);
198 	if (fontImage2x != NULL)
199 	{
200 		modelViewer->setFont2x(fontImage2x);
201 		fontImage2x->release();
202 	}
203 	windowStyle = windowStyle & ~WS_VISIBLE;
204 	inputHandler = modelViewer->getInputHandler();
205 	TCAlertManager::registerHandler(LDLError::alertClass(), this,
206 		(TCAlertCallback)&ModelWindow::ldlErrorCallback);
207 	TCAlertManager::registerHandler(TCProgressAlert::alertClass(), this,
208 		(TCAlertCallback)&ModelWindow::progressAlertCallback);
209 	TCAlertManager::registerHandler(LDrawModelViewer::alertClass(), this,
210 		(TCAlertCallback)&ModelWindow::modelViewerAlertCallback);
211 	TCAlertManager::registerHandler(LDrawModelViewer::redrawAlertClass(), this,
212 		(TCAlertCallback)&ModelWindow::redrawAlertCallback);
213 	TCAlertManager::registerHandler(LDrawModelViewer::loadAlertClass(), this,
214 		(TCAlertCallback)&ModelWindow::loadAlertCallback);
215 	TCAlertManager::registerHandler(LDInputHandler::captureAlertClass(), this,
216 		(TCAlertCallback)&ModelWindow::captureAlertCallback);
217 	TCAlertManager::registerHandler(LDInputHandler::releaseAlertClass(), this,
218 		(TCAlertCallback)&ModelWindow::releaseAlertCallback);
219 	TCAlertManager::registerHandler(LDSnapshotTaker::alertClass(), this,
220 		(TCAlertCallback)&ModelWindow::snapshotTakerAlertCallback);
221 	if (programPath != NULL)
222 	{
223 		TCUserDefaults::setStringForKey(programPath, INSTALL_PATH_KEY, false);
224 		TCUserDefaults::setStringForKey(programPath, INSTALL_PATH_4_1_KEY, false);
225 		delete[] programPath;
226 	}
227 #if defined(USE_CPP11) || !defined(_NO_BOOST)
228 	if (remoteListener)
229 	{
230 		launchRemoteListener();
231 	}
232 #endif // !_NO_BOOST
233 }
234 
~ModelWindow(void)235 ModelWindow::~ModelWindow(void)
236 {
237 }
238 
dealloc(void)239 void ModelWindow::dealloc(void)
240 {
241 #if defined(USE_CPP11) || !defined(_NO_BOOST)
242 	if (remoteListener)
243 	{
244 		shutDownRemoteListener();
245 	}
246 #endif // !_NO_BOOST
247 	TCAlertManager::unregisterHandler(this);
248 	TCObject::release(errorInfos);
249 	TCObject::release(snapshotTaker);
250 	errorInfos = NULL;
251 	if (prefs)
252 	{
253 		prefs->release();
254 		prefs = NULL;
255 	}
256 	if (errors)
257 	{
258 		errors->release();
259 		errors = NULL;
260 	}
261 	if (modelViewer)
262 	{
263 		modelViewer->release();
264 		modelViewer = NULL;
265 	}
266 	if (errorWindowResizer)
267 	{
268 		errorWindowResizer->release();
269 	}
270 	delete saveStepSuffix;
271 	stopPolling();
272 	CUIOGLWindow::dealloc();
273 }
274 
275 #if defined(USE_CPP11) || !defined(_NO_BOOST)
276 
277 #define PIPE_BUFSIZE 4096
278 #define PIPE_FILENAME _UC("\\\\.\\pipe\\LDViewRemoteControl")
279 
shutDownRemoteListener(void)280 void ModelWindow::shutDownRemoteListener(void)
281 {
282 #ifdef USE_CPP11
283 	std::unique_lock<std::mutex> lock(mutex);
284 #else
285 	boost::mutex::scoped_lock lock(mutex);
286 #endif
287 	exiting = true;
288 	lock.unlock();
289 	// Connect to the pipe to pull the ConnectNamedPipe
290 	HANDLE hPipe = CreateFile(
291 		PIPE_FILENAME,
292 		GENERIC_WRITE,
293 		0,
294 		NULL,
295 		OPEN_EXISTING,
296 		0,
297 		NULL);
298 	if (hPipe != INVALID_HANDLE_VALUE)
299 	{
300 		CloseHandle(hPipe);
301 	}
302 #ifdef USE_CPP11
303 	if (listenerThread != NULL)
304 	{
305 		if (listenerFuture.wait_for(std::chrono::seconds(1)) == std::future_status::ready)
306 		{
307 			listenerThread->join();
308 		}
309 		else
310 		{
311 			// If it hasn't shut down after 1 second, abandon it.
312 			listenerThread->detach();
313 		}
314 	}
315 #else
316 	if (!listenerThread->timed_join(boost::posix_time::seconds(1)))
317 	{
318 		// If it hasn't shut down after 1 second, abandon it.
319 		listenerThread->detach();
320 	}
321 #endif
322 	delete listenerThread;
323 }
324 
launchRemoteListener(void)325 void ModelWindow::launchRemoteListener(void)
326 {
327 	remoteCommandMap["highlight_line"] = RCHighlightLine;
328 	remoteCommandMap["highlight_lines"] = RCHighlightLine;
329 	remoteCommandMap["get_version"] = RCGetVersion;
330 	ucstringtoutf8(ldviewVersion,
331 		LDViewWindow::getProductVersion());
332 	exiting = false;
333 	remoteMessageID = RegisterWindowMessage(_UC("LDViewRemoteControl"));
334 	try
335 	{
336 #ifdef USE_CPP11
337 		listenerFuture = listenerPromise.get_future();
338 		listenerThread = new std::thread(&ModelWindow::listenerProc, this);
339 #else
340 		listenerThread = new boost::thread(
341 			boost::bind(&ModelWindow::listenerProc, this));
342 #endif
343 	}
344 	catch (...)
345 	{
346 		debugPrintf("error spawning listener thread");
347 	}
348 }
349 
listenerProc(void)350 void ModelWindow::listenerProc(void)
351 {
352 	listenerProcInner();
353 #ifdef USE_CPP11
354 	listenerPromise.set_value(true);
355 #endif
356 }
357 
listenerProcInner(void)358 void ModelWindow::listenerProcInner(void)
359 {
360 	while (true)
361 	{
362 #ifdef USE_CPP11
363 		std::unique_lock<std::mutex> lock(mutex);
364 #else
365 		boost::mutex::scoped_lock lock(mutex);
366 #endif
367 		if (exiting)
368 		{
369 			return;
370 		}
371 		lock.unlock();
372 		HANDLE hPipe = CreateNamedPipe(
373 			PIPE_FILENAME,
374 			PIPE_ACCESS_DUPLEX,
375 			PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
376 			PIPE_UNLIMITED_INSTANCES,
377 			PIPE_BUFSIZE,
378 			PIPE_BUFSIZE,
379 			NMPWAIT_USE_DEFAULT_WAIT,
380 			NULL);
381 		if (hPipe == INVALID_HANDLE_VALUE)
382 		{
383 			debugPrintf("Error creating listener pipe.\n");
384 			return;
385 		}
386 		if (!ConnectNamedPipe(hPipe, NULL))
387 		{
388 			if (GetLastError() != ERROR_PIPE_CONNECTED)
389 			{
390 				debugPrintf("Error connecting listener pipe to client.\n");
391 				CloseHandle(hPipe);
392 				return;
393 			}
394 		}
395 		lock.lock();
396 		if (exiting)
397 		{
398 			CloseHandle(hPipe);
399 			return;
400 		}
401 #ifdef USE_CPP11
402 		std::thread remoteThread(&ModelWindow::remoteProc, this, hPipe);
403 #else
404 		boost::thread remoteThread(boost::bind(&ModelWindow::remoteProc, this,
405 			hPipe));
406 #endif
407 		remoteThread.detach();
408 	}
409 }
410 
sendResponseMessage(HANDLE hPipe,const char * message)411 void ModelWindow::sendResponseMessage(HANDLE hPipe, const char *message)
412 {
413 	DWORD numWritten;
414 	DWORD messageLength = (DWORD)strlen(message);
415 
416 	// Note null terminator on message is optional.
417 	if (WriteFile(hPipe, &messageLength, sizeof(messageLength), &numWritten,
418 		NULL) && numWritten == sizeof(messageLength))
419 	{
420 		WriteFile(hPipe, message, messageLength, &numWritten, NULL);
421 	}
422 }
423 
sendVersionResponse(HANDLE hPipe)424 void ModelWindow::sendVersionResponse(HANDLE hPipe)
425 {
426 	sendResponseMessage(hPipe, ldviewVersion.c_str());
427 }
428 
remoteProc(HANDLE hPipe)429 void ModelWindow::remoteProc(HANDLE hPipe)
430 {
431 	while (true)
432 	{
433 		DWORD messageSize;
434 		DWORD readSize;
435 		char *message;
436 
437 		if (ReadFile(hPipe, &messageSize, sizeof(messageSize), &readSize, NULL))
438 		{
439 			if (readSize != sizeof(messageSize))
440 			{
441 				debugPrintf("Remote message size failure.\n");
442 				break;
443 			}
444 		}
445 		else
446 		{
447 			break;
448 		}
449 		if (messageSize > 1000000)
450 		{
451 			// Don't even try to handle unreasonable messages.
452 			break;
453 		}
454 		message = new char[messageSize + 1];
455 		if (ReadFile(hPipe, message, messageSize, &readSize, NULL))
456 		{
457 			std::string command;
458 			std::string data;
459 
460 			message[messageSize] = 0;
461 			switch (parseRemoteMessage(message, command, data))
462 			{
463 			case RCGetVersion:
464 				sendVersionResponse(hPipe);
465 				delete message;
466 				break;
467 			default:
468 				// Note: message is deleted by UI thread.
469 				PostMessage(hWindow, remoteMessageID, 0, (LPARAM)message);
470 				break;
471 			}
472 		}
473 		else
474 		{
475 			delete message;
476 			break;
477 		}
478 	}
479 	CloseHandle(hPipe);
480 }
481 
highlightLines(const std::string & paths)482 void ModelWindow::highlightLines(const std::string &paths)
483 {
484 	modelViewer->setHighlightPaths(paths);
485 }
486 
parseRemoteMessage(const char * message,std::string & command,std::string & data)487 ModelWindow::RemoteCommands ModelWindow::parseRemoteMessage(
488 	const char *message,
489 	std::string &command,
490 	std::string &data)
491 {
492 	const char *colonSpot = strchr(message, ':');
493 	if (colonSpot != NULL)
494 	{
495 		command = message;
496 		command.resize(colonSpot - message);
497 		convertStringToLower(&command[0]);
498 		data = &colonSpot[1];
499 		return remoteCommandMap[command];
500 	}
501 	return RCUnknown;
502 }
503 
processRemoteMessage(char * message)504 void ModelWindow::processRemoteMessage(char *message)
505 {
506 	std::string command;
507 	std::string data;
508 
509 	switch (parseRemoteMessage(message, command, data))
510 	{
511 	case RCHighlightLine:
512 		highlightLines(data);
513 		break;
514 	default:
515 		printf("Unrecognized remote command: %s(%s)\n", command.c_str(),
516 			data.c_str());
517 		break;
518 	}
519 	delete message;
520 }
521 
522 #endif // !_NO_BOOST
523 
ldlErrorCallback(LDLError * error)524 void ModelWindow::ldlErrorCallback(LDLError *error)
525 {
526 	if (error && userLoad)
527 	{
528 		if (!errorCallback(error))
529 		{
530 			error->cancelLoad();
531 		}
532 	}
533 }
534 
loadAlertCallback(TCAlert * alert)535 void ModelWindow::loadAlertCallback(TCAlert *alert)
536 {
537 	if (strcmp(alert->getMessage(), "ModelLoadCanceled") == 0)
538 	{
539 		parentWindow->setTitle(_UC("LDView"));
540 		stopPolling();
541 	}
542 	TCAlertManager::sendAlert(alertClass(), this, alert->getMessageUC());
543 }
544 
redrawAlertCallback(TCAlert * alert)545 void ModelWindow::redrawAlertCallback(TCAlert *alert)
546 {
547 	if (alert->getSender() == modelViewer)
548 	{
549 		redrawRequested = true;
550 		forceRedraw();
551 	}
552 }
553 
captureAlertCallback(TCAlert * alert)554 void ModelWindow::captureAlertCallback(TCAlert *alert)
555 {
556 	if (alert->getSender() == inputHandler)
557 	{
558 		captureMouse();
559 	}
560 }
561 
releaseAlertCallback(TCAlert * alert)562 void ModelWindow::releaseAlertCallback(TCAlert *alert)
563 {
564 	if (alert->getSender() == inputHandler)
565 	{
566 		releaseMouse();
567 	}
568 }
569 
snapshotTakerAlertCallback(TCAlert * alert)570 void ModelWindow::snapshotTakerAlertCallback(TCAlert *alert)
571 {
572 	if (alert->getSender() == snapshotTaker)
573 	{
574 		if (strcmp(alert->getMessage(), "MakeCurrent") == 0)
575 		{
576 			makeCurrent();
577 		}
578 	}
579 }
580 
modelViewerAlertCallback(TCAlert * alert)581 void ModelWindow::modelViewerAlertCallback(TCAlert *alert)
582 {
583 	if (alert)
584 	{
585 		stopAnimation();
586 		MessageBox(hWindow, alert->getMessageUC(), _UC("LDView"),
587 			MB_OK | MB_ICONWARNING);
588 	}
589 }
590 
progressAlertCallback(TCProgressAlert * alert)591 void ModelWindow::progressAlertCallback(TCProgressAlert *alert)
592 {
593 	if (alert)
594 	{
595 #if defined(USE_CPP11) || !defined(_NO_BOOST)
596 		if (strcmp(alert->getSource(), LD_LIBRARY_UPDATER) != 0)
597 #endif //_NO_BOOST
598 		{
599 			bool showErrors = true;
600 
601 			if (strcmp(alert->getSource(), "LDSnapshotTaker") == 0)
602 			{
603 				showErrors = false;
604 			}
605 			if (!progressCallback(alert->getMessageUC(), alert->getProgress(),
606 				strcmp(alert->getSource(), "TCImageFormat") == 0, showErrors))
607 			{
608 				alert->abort();
609 			}
610 //			printf("Progress message from %s:\n%s (%f)\n", alert->getSource(),
611 //				alert->getMessage(), alert->getProgress());
612 		}
613 	}
614 }
615 
loadSettings(void)616 void ModelWindow::loadSettings(void)
617 {
618 	pollSetting = TCUserDefaults::longForKey(POLL_KEY, 0, false);
619 	viewMode = (LDInputHandler::ViewMode)TCUserDefaults::longForKey(
620 		VIEW_MODE_KEY, 0, false);
621 	loadPrintSettings();
622 	loadSaveSettings();
623 }
624 
loadPrintSettings(void)625 void ModelWindow::loadPrintSettings(void)
626 {
627 	printLeftMargin = TCUserDefaults::longForKey(LEFT_MARGIN_KEY, 500, false) /
628 		1000.0f;
629 	printRightMargin = TCUserDefaults::longForKey(RIGHT_MARGIN_KEY, 500,
630 		false) / 1000.0f;
631 	printTopMargin = TCUserDefaults::longForKey(TOP_MARGIN_KEY, 500, false) /
632 		1000.0f;
633 	printBottomMargin = TCUserDefaults::longForKey(BOTTOM_MARGIN_KEY, 500,
634 		false) / 1000.0f;
635 	printOrientation = TCUserDefaults::longForKey(ORIENTATION_KEY,
636 		DMORIENT_PORTRAIT, false);
637 	printPaperSize = TCUserDefaults::longForKey(PAPER_SIZE_KEY, DMPAPER_LETTER,
638 		false);
639 	usePrinterDPI = TCUserDefaults::longForKey(USE_PRINTER_DPI_KEY, 1) != 0;
640 	printDPI = TCUserDefaults::longForKey(PRINT_DPI_KEY, 300);
641 	printBackground = TCUserDefaults::longForKey(PRINT_BACKGROUND_KEY, 0, false)
642 		!= 0;
643 }
644 
loadSaveSettings(void)645 void ModelWindow::loadSaveSettings(void)
646 {
647 	saveActualSize = TCUserDefaults::longForKey(SAVE_ACTUAL_SIZE_KEY, 1, false)
648 		!= 0;
649 	saveWidth = TCUserDefaults::longForKey(SAVE_WIDTH_KEY, 1024, false);
650 	saveHeight = TCUserDefaults::longForKey(SAVE_HEIGHT_KEY, 768, false);
651 	saveZoomToFit = TCUserDefaults::longForKey(SAVE_ZOOM_TO_FIT_KEY, 1, false)
652 		!= 0;
653 	saveSeries = TCUserDefaults::longForKey(SAVE_SERIES_KEY, 1, false) != 0;
654 	saveDigits = TCUserDefaults::longForKey(SAVE_DIGITS_KEY, 1, false);
655 	ignorePBuffer = TCUserDefaults::longForKey(IGNORE_PBUFFER_KEY, 0, false)
656 		!= 0;
657 	ignoreFBO = TCUserDefaults::longForKey(IGNORE_FRAMEBUFFER_OBJECT_KEY, 0,
658 		false) != 0;
659 	ignorePixelFormat = TCUserDefaults::longForKey(IGNORE_PIXEL_FORMAT_KEY, 0,
660 		false) != 0;
661 	saveImageType = TCUserDefaults::longForKey(SAVE_IMAGE_TYPE_KEY, 1, false);
662 	saveExportType = TCUserDefaults::longForKey(SAVE_EXPORT_TYPE_KEY,
663 		LDrawModelViewer::ETPov, false);
664 	saveAlpha = TCUserDefaults::longForKey(SAVE_ALPHA_KEY, 0, false) != 0;
665 	autoCrop = TCUserDefaults::boolForKey(AUTO_CROP_KEY, false, false);
666 	saveAllSteps = TCUserDefaults::boolForKey(SAVE_STEPS_KEY, false, false);
667 	delete[] saveStepSuffix;
668 	saveStepSuffix = TCUserDefaults::stringForKeyUC(SAVE_STEPS_SUFFIX_KEY,
669 		ls(_UC("DefaultStepSuffix")), false);
670 	saveStepsSameScale = TCUserDefaults::boolForKey(SAVE_STEPS_SAME_SCALE_KEY,
671 		true, false);
672 }
673 
674 // Note: static function
roundUp(int value,int nearest)675 int ModelWindow::roundUp(int value, int nearest)
676 {
677 	return (value + nearest - 1) / nearest * nearest;
678 }
679 
setFilename(const char * value)680 void ModelWindow::setFilename(const char* value)
681 {
682 	modelViewer->setFilename(value);
683 	checkForPart();
684 }
685 
getFilename(void)686 char* ModelWindow::getFilename(void)
687 {
688 	return modelViewer->getFilename();
689 }
690 
update(void)691 void ModelWindow::update(void)
692 {
693 	doPaint();
694 }
695 
finalSetup(void)696 void ModelWindow::finalSetup(void)
697 {
698 }
699 
getFileTime(FILETIME * fileTime)700 bool ModelWindow::getFileTime(FILETIME* fileTime)
701 {
702 	return getFileInfo(fileTime, NULL, NULL);
703 }
704 
getFileInfo(FILETIME * fileTime,DWORD * fileSizeHigh,DWORD * fileSizeLow)705 bool ModelWindow::getFileInfo(FILETIME* fileTime, DWORD* fileSizeHigh, DWORD* fileSizeLow)
706 {
707 	const char *filename;
708 
709 	if (modelViewer && (filename = modelViewer->getFilename()) != NULL)
710 	{
711 		WIN32_FIND_DATA findBuf;
712 		HANDLE findHandle;
713 		ucstring ucFilename;
714 
715 		utf8toucstring(ucFilename, filename);
716 		findHandle = FindFirstFile(ucFilename.c_str(), &findBuf);
717 		if (findHandle != INVALID_HANDLE_VALUE)
718 		{
719 			if (fileTime != NULL)
720 			{
721 				*fileTime = findBuf.ftLastWriteTime;
722 			}
723 			if (fileSizeHigh != NULL && fileSizeLow != NULL)
724 			{
725 				*fileSizeHigh = findBuf.nFileSizeHigh;
726 				*fileSizeLow = findBuf.nFileSizeLow;
727 			}
728 			FindClose(findHandle);
729 			return true;
730 		}
731 	}
732 	return false;
733 }
734 
checkFileForUpdates(void)735 void ModelWindow::checkFileForUpdates(void)
736 {
737 	if (pollTimerRunning)
738 	{
739 		FILETIME newWriteTime;
740 		DWORD newFileSizeHigh, newFileSizeLow;
741 
742 		stopPolling();
743 		if (getFileInfo(&newWriteTime, &newFileSizeHigh, &newFileSizeLow))
744 		{
745 			if (lastWriteTime.dwLowDateTime != newWriteTime.dwLowDateTime ||
746 				lastWriteTime.dwHighDateTime != newWriteTime.dwHighDateTime)
747 			{
748 				if (newFileSizeHigh != lastFileSizeHigh || newFileSizeLow != lastFileSizeLow)
749 				{
750 					// A full half second must pass without any changes to the file
751 					// size before we consider the file update to be complete.
752 					startPolling();
753 					lastFileSizeHigh = newFileSizeHigh;
754 					lastFileSizeLow = newFileSizeLow;
755 					return;
756 				}
757 				lastFileSizeHigh = lastFileSizeLow = 0;
758 				bool update = true;
759 
760 				lastWriteTime = newWriteTime;
761 				if (pollSetting == POLL_PROMPT)
762 				{
763 					if (captureCount)
764 					{
765 						while (captureCount)
766 						{
767 							releaseMouse();
768 						}
769 						inputHandler->cancelMouseDrag();
770 					}
771 					stopAnimation();
772 					if (MessageBox(hWindow, ls(_UC("PollReloadCheck")),
773 						ls(_UC("PollFileUpdate")),
774 						MB_OKCANCEL | MB_APPLMODAL | MB_ICONQUESTION) !=
775 						IDOK)
776 					{
777 						update = false;
778 					}
779 				}
780 				if (update)
781 				{
782 					reload();
783 				}
784 			}
785 		}
786 		startPolling();
787 	}
788 }
789 
openGlWillEnd(void)790 void ModelWindow::openGlWillEnd(void)
791 {
792 	if (modelViewer)
793 	{
794 		modelViewer->openGlWillEnd();
795 	}
796 }
797 
updateFSAA()798 void ModelWindow::updateFSAA()
799 {
800 	applyingPrefs = true;
801 	uncompile();
802 	closeWindow();
803 	if (!((LDViewWindow*)parentWindow)->getFullScreen())
804 	{
805 		x -= 2;
806 		y -= 2;
807 		width += 4;
808 		height += 4;
809 	}
810 	initWindow();
811 	if (modelViewer)
812 	{
813 		modelViewer->uncompile();
814 	}
815 	showWindow(SW_SHOW);
816 	applyingPrefs = false;
817 	killTimer(FSAA_UPDATE_TIMER);
818 	forceRedraw();
819 	if (hStatusBar)
820 	{
821 		// For some unknown reason, the status bar only updates its text on the
822 		// fly while the model is rotating if it is created after the model
823 		// window.  Since we just destroyed and recreated the model window, we
824 		// need to destroy and recreate the status window.
825 		((LDViewWindow*)parentWindow)->switchStatusBar();
826 		((LDViewWindow*)parentWindow)->switchStatusBar();
827 	}
828 }
829 
doTimer(UINT_PTR timerID)830 LRESULT ModelWindow::doTimer(UINT_PTR timerID)
831 {
832 	switch (timerID)
833 	{
834 	case POLL_TIMER:
835 		checkFileForUpdates();
836 		break;
837 	case FSAA_UPDATE_TIMER:
838 		updateFSAA();
839 		break;
840 	}
841 	return 0;
842 }
843 
captureMouse(void)844 void ModelWindow::captureMouse(void)
845 {
846 	SetCapture(hWindow);
847 }
848 
releaseMouse(void)849 void ModelWindow::releaseMouse(void)
850 {
851 	releasingMouse = true;
852 	ReleaseCapture();
853 	releasingMouse = false;
854 }
855 
856 // NOTE: Static method.
altPressed(void)857 bool ModelWindow::altPressed(void)
858 {
859 	return (GetKeyState(VK_MENU) & 0x8000) != 0;
860 }
861 
862 // Note: static method.
convertKeyModifiers(TCULong osModifiers)863 TCULong ModelWindow::convertKeyModifiers(TCULong osModifiers)
864 {
865 	TCULong retValue = 0;
866 
867 	if (osModifiers & MK_SHIFT)
868 	{
869 		retValue |= LDInputHandler::MKShift;
870 	}
871 	if (osModifiers & MK_CONTROL)
872 	{
873 		retValue |= LDInputHandler::MKControl;
874 	}
875 	return retValue;
876 }
877 
878 // Note: static method.
getCurrentKeyModifiers(void)879 TCULong ModelWindow::getCurrentKeyModifiers(void)
880 {
881 	TCULong retValue = 0;
882 
883 	if (GetKeyState(VK_SHIFT) & 0x8000)
884 	{
885 		retValue |= LDInputHandler::MKShift;
886 	}
887 	if (GetKeyState(VK_CONTROL) & 0x8000)
888 	{
889 		retValue |= LDInputHandler::MKControl;
890 	}
891 	return retValue;
892 }
893 
894 // Note: static method.
convertKeyCode(TCULong osKeyCode)895 LDInputHandler::KeyCode ModelWindow::convertKeyCode(TCULong osKeyCode)
896 {
897 	if (isalpha(osKeyCode))
898 	{
899 		return (LDInputHandler::KeyCode)(toupper(osKeyCode) - 'A' +
900 			LDInputHandler::KCA);
901 	}
902 	else
903 	{
904 		switch (osKeyCode)
905 		{
906 		case VK_RETURN:
907 			return LDInputHandler::KCReturn;
908 		case VK_SHIFT:
909 			return LDInputHandler::KCShift;
910 		case VK_CONTROL:
911 			return LDInputHandler::KCControl;
912 		case VK_MENU:
913 			return LDInputHandler::KCAlt;
914 		case VK_SPACE:
915 			return LDInputHandler::KCSpace;
916 		case VK_PRIOR:
917 			return LDInputHandler::KCPageUp;
918 		case VK_NEXT:
919 			return LDInputHandler::KCPageDown;
920 		case VK_END:
921 			return LDInputHandler::KCEnd;
922 		case VK_HOME:
923 			return LDInputHandler::KCHome;
924 		case VK_ESCAPE:
925 			return LDInputHandler::KCEscape;
926 		case VK_LEFT:
927 			return LDInputHandler::KCLeft;
928 		case VK_UP:
929 			return LDInputHandler::KCUp;
930 		case VK_RIGHT:
931 			return LDInputHandler::KCRight;
932 		case VK_DOWN:
933 			return LDInputHandler::KCDown;
934 		case VK_INSERT:
935 			return LDInputHandler::KCInsert;
936 		case VK_DELETE:
937 			return LDInputHandler::KCDelete;
938 		default:
939 			return LDInputHandler::KCUnknown;
940 		}
941 	}
942 }
943 
doLButtonDown(WPARAM keyFlags,int xPos,int yPos)944 LRESULT ModelWindow::doLButtonDown(WPARAM keyFlags, int xPos, int yPos)
945 {
946 	if (inputHandler->mouseDown(convertKeyModifiers((TCULong)keyFlags),
947 		LDInputHandler::MBLeft, unscalePixels(xPos), unscalePixels(yPos)))
948 	{
949 		return 0;
950 	}
951 	return 1;
952 }
953 
doLButtonUp(WPARAM keyFlags,int xPos,int yPos)954 LRESULT ModelWindow::doLButtonUp(WPARAM keyFlags, int xPos, int yPos)
955 {
956 	if (inputHandler->mouseUp((TCULong)keyFlags, LDInputHandler::MBLeft,
957 		unscalePixels(xPos), unscalePixels(yPos)))
958 	{
959 		return 0;
960 	}
961 	return 1;
962 }
963 
doRButtonDown(WPARAM keyFlags,int xPos,int yPos)964 LRESULT ModelWindow::doRButtonDown(WPARAM keyFlags, int xPos, int yPos)
965 {
966 	if (inputHandler->mouseDown(convertKeyModifiers((TCULong)keyFlags),
967 		LDInputHandler::MBRight, unscalePixels(xPos), unscalePixels(yPos)))
968 	{
969 		return 0;
970 	}
971 	return 1;
972 }
973 
doRButtonUp(WPARAM keyFlags,int xPos,int yPos)974 LRESULT ModelWindow::doRButtonUp(WPARAM keyFlags, int xPos, int yPos)
975 {
976 	if (inputHandler->mouseUp((TCULong)keyFlags, LDInputHandler::MBRight,
977 		unscalePixels(xPos), unscalePixels(yPos)))
978 	{
979 		return 0;
980 	}
981 	return 1;
982 }
983 
doMButtonDown(WPARAM keyFlags,int xPos,int yPos)984 LRESULT ModelWindow::doMButtonDown(WPARAM keyFlags, int xPos, int yPos)
985 {
986 	if (inputHandler->mouseDown(convertKeyModifiers((TCULong)keyFlags),
987 		LDInputHandler::MBMiddle, unscalePixels(xPos), unscalePixels(yPos)))
988 	{
989 		return 0;
990 	}
991 	return 1;
992 }
993 
doMButtonUp(WPARAM keyFlags,int xPos,int yPos)994 LRESULT ModelWindow::doMButtonUp(WPARAM keyFlags, int xPos, int yPos)
995 {
996 	if (inputHandler->mouseUp((TCULong)keyFlags, LDInputHandler::MBMiddle,
997 		unscalePixels(xPos), unscalePixels(yPos)))
998 	{
999 		return 0;
1000 	}
1001 	return 1;
1002 }
1003 
doCaptureChanged(HWND)1004 LRESULT ModelWindow::doCaptureChanged(HWND /*hNewWnd*/)
1005 {
1006 	if (!releasingMouse && inputHandler->mouseCaptureChanged())
1007 	{
1008 		return 0;
1009 	}
1010 	return 1;
1011 }
1012 
doMouseMove(WPARAM keyFlags,int xPos,int yPos)1013 LRESULT ModelWindow::doMouseMove(WPARAM keyFlags, int xPos, int yPos)
1014 {
1015 	if (inputHandler->mouseMove(convertKeyModifiers((TCULong)keyFlags),
1016 		unscalePixels(xPos), unscalePixels(yPos)))
1017 	{
1018 		return 0;
1019 	}
1020 	return 1;
1021 }
1022 
mouseWheel(short keyFlags,short zDelta)1023 void ModelWindow::mouseWheel(short keyFlags, short zDelta)
1024 {
1025 	inputHandler->mouseWheel(convertKeyModifiers(keyFlags),
1026 		(TCFloat)zDelta);
1027 }
1028 
doEraseBackground(RECT *)1029 LRESULT ModelWindow::doEraseBackground(RECT* /*updateRect*/)
1030 {
1031 	created = TRUE;
1032 	return 1;
1033 }
1034 
doSize(WPARAM sizeType,int newWidth,int newHeight)1035 LRESULT ModelWindow::doSize(WPARAM sizeType, int newWidth, int newHeight)
1036 {
1037 	RECT rect;
1038 	POINT point;
1039 	LRESULT retValue;
1040 
1041 	GetWindowRect(hWindow, &rect);
1042 	point.x = rect.left;
1043 	point.y = rect.top;
1044 	ScreenToClient(hParentWindow, &point);
1045 	if (hPBufferGLRC)
1046 	{
1047 		wglMakeCurrent(hdc, hglrc);
1048 	}
1049 	retValue = CUIOGLWindow::doSize(sizeType, newWidth, newHeight);
1050 	if (modelViewer && !hPBufferGLRC)
1051 	{
1052 		updateModelViewerSize();
1053 	}
1054 	if (hPBufferGLRC)
1055 	{
1056 		makeCurrent();
1057 	}
1058 	return retValue;
1059 }
1060 
sizeView(void)1061 void ModelWindow::sizeView(void)
1062 {
1063 }
1064 
perspectiveView(void)1065 void ModelWindow::perspectiveView(void)
1066 {
1067 	modelViewer->perspectiveView();
1068 }
1069 
forceRedraw(int count)1070 void ModelWindow::forceRedraw(int count)
1071 {
1072 	if (hWindow)
1073 	{
1074 		InvalidateRect(hWindow, NULL, FALSE);
1075 	}
1076 	redrawCount += count;
1077 }
1078 
setZoomSpeed(TCFloat value)1079 void ModelWindow::setZoomSpeed(TCFloat value)
1080 {
1081 	modelViewer->setZoomSpeed(value);
1082 }
1083 
getZoomSpeed(void)1084 TCFloat ModelWindow::getZoomSpeed(void)
1085 {
1086 	return modelViewer->getZoomSpeed();
1087 }
1088 
resetView(LDVAngle viewAngle)1089 void ModelWindow::resetView(LDVAngle viewAngle)
1090 {
1091 	modelViewer->resetView(viewAngle);
1092 	modelViewer->setXRotate(0.0f);
1093 	modelViewer->setYRotate(0.0f);
1094 	stopAnimation();
1095 	fps = 0.0f;
1096 	forceRedraw();
1097 }
1098 
saveDefaultView(void)1099 void ModelWindow::saveDefaultView(void)
1100 {
1101 	prefs->saveDefaultView();
1102 }
1103 
resetDefaultView(void)1104 void ModelWindow::resetDefaultView(void)
1105 {
1106 	prefs->resetDefaultView();
1107 }
1108 
reload(void)1109 void ModelWindow::reload(void)
1110 {
1111 	clearErrors();
1112 	modelViewer->setNeedsReload();
1113 	forceRedraw();
1114 }
1115 
recompile(void)1116 void ModelWindow::recompile(void)
1117 {
1118 	modelViewer->recompile();
1119 }
1120 
uncompile(void)1121 void ModelWindow::uncompile(void)
1122 {
1123 	if (modelViewer)
1124 	{
1125 		modelViewer->uncompile();
1126 	}
1127 }
1128 
doErrorOK(void)1129 BOOL ModelWindow::doErrorOK(void)
1130 {
1131 	doDialogClose(hErrorWindow);
1132 	return TRUE;
1133 }
1134 
doProgressCancel(void)1135 void ModelWindow::doProgressCancel(void)
1136 {
1137 	cancelLoad = true;
1138 }
1139 
getTreeViewLine(HWND hTreeView,HTREEITEM hItem,TCStringArray * lines)1140 void ModelWindow::getTreeViewLine(HWND hTreeView, HTREEITEM hItem,
1141 								  TCStringArray *lines)
1142 {
1143 	TVITEM item;
1144 	UCCHAR buf[1024];
1145 	ucstring line;
1146 	int depth = 0;
1147 	HTREEITEM hParentItem = hItem;
1148 
1149 	while ((hParentItem = TreeView_GetParent(hTreeView, hParentItem)) != NULL)
1150 	{
1151 		depth++;
1152 	}
1153 	item.mask = TVIF_TEXT;
1154 	item.hItem = hItem;
1155 	item.pszText = buf;
1156 	item.cchTextMax = COUNT_OF(buf);
1157 	if (TreeView_GetItem(hTreeView, &item))
1158 	{
1159 		int i;
1160 
1161 		for (i = 0; i < depth; i++)
1162 		{
1163 			line += _UC('\t');
1164 		}
1165 		line += item.pszText;
1166 		std::string utf8Buf;
1167 		ucstringtoutf8(utf8Buf, line);
1168 		lines->addString(utf8Buf.c_str());
1169 	}
1170 }
1171 
doErrorTreeCopy(void)1172 BOOL ModelWindow::doErrorTreeCopy(void)
1173 {
1174 	HTREEITEM hItem = TreeView_GetSelection(hErrorTree);
1175 
1176 	if (hItem)
1177 	{
1178 		HTREEITEM hParentItem = TreeView_GetParent(hErrorTree, hItem);
1179 		TCStringArray* textLines = new TCStringArray;
1180 		int i;
1181 		int count;
1182 
1183 		while (hParentItem)
1184 		{
1185 			hItem = hParentItem;
1186 			hParentItem = TreeView_GetParent(hErrorTree, hItem);
1187 		}
1188 		getTreeViewLine(hErrorTree, hItem, textLines);
1189 		hItem = TreeView_GetChild(hErrorTree, hItem);
1190 		while (hItem)
1191 		{
1192 			getTreeViewLine(hErrorTree, hItem, textLines);
1193 			hItem = TreeView_GetNextSibling(hErrorTree, hItem);
1194 		}
1195 		count = textLines->getCount();
1196 		if (count)
1197 		{
1198 			size_t len = 1;
1199 			char *buf;
1200 
1201 			for (i = 0; i < count; i++)
1202 			{
1203 				len += strlen((*textLines)[i]) + 2;
1204 			}
1205 			buf = new char[len];
1206 			buf[0] = 0;
1207 			for (i = 0; i < count; i++)
1208 			{
1209 				strcat(buf, (*textLines)[i]);
1210 				strcat(buf, "\r\n");
1211 			}
1212 			if (copyToClipboard(buf))
1213 			{
1214 				delete buf;
1215 				SetWindowLongPtr(hErrorWindow, DWLP_MSGRESULT, TRUE);
1216 				return TRUE;
1217 			}
1218 			delete buf;
1219 		}
1220 	}
1221 	return FALSE;
1222 }
1223 
doErrorTreeKeyDown(LPNMTVKEYDOWN notification)1224 BOOL ModelWindow::doErrorTreeKeyDown(LPNMTVKEYDOWN notification)
1225 {
1226 //	debugPrintf("ModelWindow::doErrorTreeKeyDown: 0x%08X, 0x%04X, 0x%08X\n",
1227 //		notification->hdr.code, notification->wVKey, notification->flags);
1228 	if (notification->wVKey == 'C' && (GetKeyState(VK_CONTROL) & 0x8000))
1229 	{
1230 		return doErrorTreeCopy();
1231 	}
1232 	return FALSE;
1233 }
1234 
doErrorWindowNotify(LPNMHDR notification)1235 BOOL ModelWindow::doErrorWindowNotify(LPNMHDR notification)
1236 {
1237 //	debugPrintf("ModelWindow::doErrorWindowNotify: %d\n", notification->code);
1238 	switch (notification->code)
1239 	{
1240 	case LVN_ITEMCHANGED:
1241 		LPNMLISTVIEW info = (LPNMLISTVIEW)notification;
1242 		if (info->uNewState != info->uOldState)
1243 		{
1244 			BOOL value = ListView_GetCheckState(hErrorList, info->iItem);
1245 			ErrorInfo *errorInfo = (*errorInfos)[info->iItem];
1246 			char *errorKey = getErrorKey(errorInfo->getType());
1247 
1248 			// The new state/old state check lies when you click on an item, and
1249 			// not the item's check box.
1250 			if (TCUserDefaults::longForKey(errorKey, 0, false) != value)
1251 			{
1252 				TCUserDefaults::setLongForKey(value, errorKey, false);
1253 				if (!skipErrorUpdates)
1254 				{
1255 					clearErrorTree();
1256 					populateErrorTree();
1257 				}
1258 			}
1259 		}
1260 		break;
1261 	}
1262 	return FALSE;
1263 }
1264 
doErrorTreeNotify(LPNMHDR notification)1265 BOOL ModelWindow::doErrorTreeNotify(LPNMHDR notification)
1266 {
1267 //	debugPrintf("ModelWindow::doErrorTreeNotify: %d\n", notification->code);
1268 	if (notification->code == NM_DBLCLK)
1269 	{
1270 		HTREEITEM hSelectedItem = TreeView_GetSelection(hErrorTree);
1271 
1272 //		debugPrintf("double click!\n");
1273 		if (hSelectedItem)
1274 		{
1275 			TVITEM selectedItem;
1276 			UCCHAR buf[1024];
1277 
1278 			selectedItem.mask = TVIF_TEXT;
1279 			selectedItem.hItem = hSelectedItem;
1280 			selectedItem.pszText = buf;
1281 			selectedItem.cchTextMax = COUNT_OF(buf);
1282 			if (TreeView_GetItem(hErrorTree, &selectedItem))
1283 			{
1284 				if (stringHasPrefix(selectedItem.pszText,
1285 					ls(_UC("ErrorTreeFilePrefix"))))
1286 				{
1287 					UCSTR editor = TCUserDefaults::stringForKeyUC(EDITOR_KEY,
1288 						_UC("notepad.exe"), false);
1289 
1290 					ShellExecute(hWindow, NULL, editor, buf + 6, _UC("."),
1291 						SW_SHOWNORMAL);
1292 					delete[] editor;
1293 				}
1294 			}
1295 		}
1296 		else
1297 		{
1298 //			debugPrintf("No selection.\n");
1299 		}
1300 	}
1301 	else if (notification->code == TVN_KEYDOWN)
1302 	{
1303 		return doErrorTreeKeyDown((LPNMTVKEYDOWN)notification);
1304 	}
1305 	return FALSE;
1306 }
1307 
doDialogNotify(HWND hDlg,int controlId,LPNMHDR notification)1308 BOOL ModelWindow::doDialogNotify(HWND hDlg, int controlId, LPNMHDR notification)
1309 {
1310 //	debugPrintf("ModelWindow::doDialogNotify: 0x%04X, 0x%04X, 0x%04x\n", hDlg,
1311 //		controlId, notification->code);
1312 	if (hDlg == hErrorWindow)
1313 	{
1314 		if (controlId == IDC_ERROR_TREE)
1315 		{
1316 			return doErrorTreeNotify(notification);
1317 		}
1318 		else
1319 		{
1320 			return doErrorWindowNotify(notification);
1321 		}
1322 	}
1323 	else if (hDlg == hSaveDialog)
1324 	{
1325 		return doSaveNotify(controlId, (LPOFNOTIFY)notification);
1326 	}
1327 	return FALSE;
1328 }
1329 
doErrorSize(WPARAM sizeType,int newWidth,int newHeight)1330 BOOL ModelWindow::doErrorSize(WPARAM sizeType, int newWidth, int newHeight)
1331 {
1332 	//RECT sizeRect;
1333 
1334 	SendMessage(hErrorStatusWindow, WM_SIZE, sizeType,
1335 		MAKELPARAM(newWidth, newHeight));
1336 	//GetClientRect(hErrorWindow, &sizeRect);
1337 	errorWindowResizer->resize(newWidth, newHeight);
1338 	return FALSE;
1339 }
1340 
doSaveSize(WPARAM sizeType,int newWidth,int newHeight)1341 BOOL ModelWindow::doSaveSize(WPARAM sizeType, int newWidth, int newHeight)
1342 {
1343 	if (saveWindowResizer)
1344 	{
1345 		if (sizeType != SW_MINIMIZE)
1346 		{
1347 			saveWindowResizer->resize(newWidth, newHeight);
1348 			positionSaveAddOn();
1349 		}
1350 	}
1351 	return FALSE;
1352 }
1353 
positionSaveOptionsButton(void)1354 void ModelWindow::positionSaveOptionsButton(void)
1355 {
1356 	RECT optionsButtonRect;
1357 	RECT typeComboRect;
1358 	HWND hParent = GetParent(hSaveDialog);
1359 	HWND hTypeCombo = GetDlgItem(hParent, cmb1);
1360 	int typeComboWidth;
1361 	int typeComboHeight;
1362 	int optionsButtonWidth;
1363 	int optionsButtonHeight;
1364 
1365 	// Move the Options button so that it is to the right of the file type
1366 	// combo box.
1367 	GetWindowRect(hTypeCombo, &typeComboRect);
1368 	screenToClient(hParent, &typeComboRect);
1369 	GetWindowRect(hSaveOptionsButton, &optionsButtonRect);
1370 	optionsButtonWidth = optionsButtonRect.right - optionsButtonRect.left;
1371 	optionsButtonHeight = optionsButtonRect.bottom - optionsButtonRect.top;
1372 	typeComboWidth = typeComboRect.right - typeComboRect.left;
1373 	typeComboHeight = typeComboRect.bottom - typeComboRect.top;
1374 	clientToScreen(hParent, &typeComboRect);
1375 	screenToClient(hSaveDialog, &typeComboRect);
1376 
1377 	MoveWindow(hSaveOptionsButton, typeComboRect.right + 6,
1378 		typeComboRect.top + typeComboHeight / 2 - optionsButtonHeight / 2,
1379 		optionsButtonWidth, optionsButtonHeight, TRUE);
1380 }
1381 
positionSaveAddOn(void)1382 void ModelWindow::positionSaveAddOn(void)
1383 {
1384 	HWND hParent = GetParent(hSaveDialog);
1385 	RECT parentClientRect;
1386 	RECT addOnRect;
1387 	int windowWidth;
1388 	int windowHeight;
1389 
1390 	GetClientRect(hParent, &parentClientRect);
1391 	GetWindowRect(hSaveDialog, &addOnRect);
1392 	screenToClient(hParent, &addOnRect);
1393 	windowWidth = parentClientRect.right - parentClientRect.left;
1394 	windowHeight = parentClientRect.bottom - parentClientRect.top;
1395 	if (addOnRect.left != 0 || addOnRect.top != 0 ||
1396 		addOnRect.right != windowWidth || addOnRect.bottom != windowHeight)
1397 	{
1398 		// Only move it if it has actually changed position.  That's because
1399 		// this function is called from doSaveSize, and we don't want to go
1400 		// into an infinite loop.
1401 		MoveWindow(hSaveDialog, 0, 0, windowWidth, windowHeight, TRUE);
1402 	}
1403 	positionSaveOptionsButton();
1404 }
1405 
doSaveInitDone(OFNOTIFY *)1406 BOOL ModelWindow::doSaveInitDone(OFNOTIFY * /*ofNotify*/)
1407 {
1408 	RECT optionsButtonRect;
1409 	RECT typeComboRect;
1410 	RECT miscRect;
1411 	RECT parentRect;
1412 	RECT cancelRect;
1413 	RECT windowRect;
1414 	HWND hParent = GetParent(hSaveDialog);
1415 	HWND hMiscBox = GetDlgItem(hSaveDialog, IDC_MISC_BOX);
1416 	HWND hCancelButton = GetDlgItem(hParent, IDCANCEL);
1417 	HWND hTypeCombo = GetDlgItem(hParent, cmb1);
1418 	int typeComboWidth;
1419 	int typeComboHeight;
1420 	int optionsButtonWidth;
1421 
1422 	// Shrink the type combo to make space for the options button.
1423 	GetWindowRect(hTypeCombo, &typeComboRect);
1424 	GetWindowRect(hSaveOptionsButton, &optionsButtonRect);
1425 	screenToClient(hParent, &typeComboRect);
1426 	optionsButtonWidth = optionsButtonRect.right - optionsButtonRect.left;
1427 	typeComboWidth = typeComboRect.right - typeComboRect.left
1428 		- optionsButtonWidth - 6;
1429 	typeComboHeight = typeComboRect.bottom - typeComboRect.top;
1430 	MoveWindow(hTypeCombo, typeComboRect.left, typeComboRect.top,
1431 		typeComboWidth, typeComboHeight, TRUE);
1432 
1433 	// Resize the add-on window so that the right margin between the misc box
1434 	// and the add-on window is the same as the right margin between the save
1435 	// dialog's cancel button and the save dialog.  That way, the resizer will
1436 	// consider that to be the "proper" right margin, and when we resize the
1437 	// add-on to fill the available space, everything will size properly.
1438 	GetClientRect(hParent, &parentRect);
1439 	GetWindowRect(hCancelButton, &cancelRect);
1440 	screenToClient(hParent, &cancelRect);
1441 	GetWindowRect(hMiscBox, &miscRect);
1442 	screenToClient(hSaveDialog, &miscRect);
1443 	GetClientRect(hParent, &windowRect);
1444 	windowRect.right = miscRect.right + parentRect.right - cancelRect.right;
1445 	MoveWindow(hSaveDialog, 0, 0, windowRect.right, windowRect.bottom, TRUE);
1446 
1447 	saveWindowResizer = new CUIWindowResizer;
1448 	saveWindowResizer->setHWindow(hSaveDialog);
1449 
1450 	if (curSaveOp == LDPreferences::SOSnapshot)
1451 	{
1452 		saveWindowResizer->addSubWindow(IDC_SAVE_SERIES_BOX,
1453 			CUISizeHorizontal | CUIFloatRight | CUIFloatTop);
1454 		saveWindowResizer->addSubWindow(IDC_SAVE_SERIES,
1455 			CUIFloatRight | CUIFloatTop);
1456 		saveWindowResizer->addSubWindow(IDC_SAVE_DIGITS_LABEL,
1457 			CUIFloatRight | CUIFloatTop);
1458 		saveWindowResizer->addSubWindow(IDC_SAVE_DIGITS,
1459 			CUIFloatRight | CUIFloatTop);
1460 		saveWindowResizer->addSubWindow(IDC_SAVE_DIGITS_SPIN,
1461 			CUIFloatRight | CUIFloatTop);
1462 
1463 		saveWindowResizer->addSubWindow(IDC_SAVE_ACTUAL_SIZE_BOX,
1464 			CUISizeHorizontal | CUIFloatRight | CUIFloatTop);
1465 		saveWindowResizer->addSubWindow(IDC_SAVE_ACTUAL_SIZE,
1466 			CUIFloatRight | CUIFloatTop);
1467 		saveWindowResizer->addSubWindow(IDC_SAVE_WIDTH_LABEL,
1468 			CUIFloatRight | CUIFloatTop);
1469 		saveWindowResizer->addSubWindow(IDC_SAVE_WIDTH,
1470 			CUIFloatRight | CUIFloatTop);
1471 		saveWindowResizer->addSubWindow(IDC_SAVE_HEIGHT_LABEL,
1472 			CUIFloatRight | CUIFloatTop);
1473 		saveWindowResizer->addSubWindow(IDC_SAVE_HEIGHT,
1474 			CUIFloatRight | CUIFloatTop);
1475 		saveWindowResizer->addSubWindow(IDC_SAVE_ZOOMTOFIT,
1476 			CUIFloatRight | CUIFloatTop);
1477 
1478 		saveWindowResizer->addSubWindow(IDC_ALL_STEPS_BOX,
1479 			CUIFloatLeft | CUISizeHorizontal | CUIFloatTop);
1480 		saveWindowResizer->addSubWindow(IDC_STEP_SUFFIX_LABEL,
1481 			CUIFloatLeft | CUIFloatTop | CUIFloatRight);
1482 		saveWindowResizer->addSubWindow(IDC_STEP_SUFFIX,
1483 			CUIFloatLeft | CUIFloatTop | CUIFloatRight);
1484 		saveWindowResizer->addSubWindow(IDC_SAME_SCALE,
1485 			CUIFloatLeft | CUIFloatTop | CUIFloatRight);
1486 
1487 		saveWindowResizer->addSubWindow(IDC_MISC_BOX,
1488 			CUIFloatLeft | CUISizeHorizontal | CUIFloatTop);
1489 		saveWindowResizer->addSubWindow(IDC_ALL_STEPS,
1490 			CUIFloatLeft | CUIFloatTop | CUIFloatRight);
1491 		saveWindowResizer->addSubWindow(IDC_IGNORE_PBUFFER,
1492 			CUIFloatLeft | CUIFloatTop | CUIFloatRight);
1493 		saveWindowResizer->addSubWindow(IDC_IGNORE_FBO,
1494 			CUIFloatLeft | CUIFloatTop | CUIFloatRight);
1495 		saveWindowResizer->addSubWindow(IDC_IGNORE_PIXEL_FORMAT,
1496 			CUIFloatLeft | CUIFloatTop | CUIFloatRight);
1497 		saveWindowResizer->addSubWindow(IDC_TRANSPARENT_BACKGROUND,
1498 			CUIFloatLeft | CUIFloatTop | CUIFloatRight);
1499 		saveWindowResizer->addSubWindow(IDC_AUTO_CROP,
1500 			CUIFloatLeft | CUIFloatTop | CUIFloatRight);
1501 	}
1502 	positionSaveAddOn();
1503 
1504 	return FALSE;
1505 }
1506 
doDialogSize(HWND hDlg,WPARAM sizeType,int newWidth,int newHeight)1507 BOOL ModelWindow::doDialogSize(HWND hDlg, WPARAM sizeType, int newWidth,
1508 							   int newHeight)
1509 {
1510 //	debugPrintf("ModelWindow::doDialogSize(%d, %d, %d)\n", sizeType, newWidth,
1511 //		newHeight);
1512 	if (hDlg == hErrorWindow)
1513 	{
1514 		return doErrorSize(sizeType, newWidth, newHeight);
1515 	}
1516 	else if (hDlg == hSaveDialog)
1517 	{
1518 		return doSaveSize(sizeType, newWidth, newHeight);
1519 	}
1520 	return FALSE;
1521 }
1522 
doDialogGetMinMaxInfo(HWND hDlg,LPMINMAXINFO minMaxInfo)1523 BOOL ModelWindow::doDialogGetMinMaxInfo(HWND hDlg, LPMINMAXINFO minMaxInfo)
1524 {
1525 	if (hDlg == hErrorWindow)
1526 	{
1527 		calcSystemSizes();
1528 		minMaxInfo->ptMaxSize.x = systemMaxWidth;
1529 		minMaxInfo->ptMaxSize.y = systemMaxHeight;
1530 		minMaxInfo->ptMinTrackSize.x = scalePoints(475);
1531 		minMaxInfo->ptMinTrackSize.y = scalePoints(260);
1532 		minMaxInfo->ptMaxTrackSize.x = systemMaxTrackWidth;
1533 		minMaxInfo->ptMaxTrackSize.y = systemMaxTrackHeight;
1534 		return TRUE;
1535 	}
1536 	return FALSE;
1537 }
1538 
doProgressClick(int controlId,HWND)1539 BOOL ModelWindow::doProgressClick(int controlId, HWND /*controlHWnd*/)
1540 {
1541 	if (controlId == IDCANCEL)
1542 	{
1543 		doProgressCancel();
1544 	}
1545 	return TRUE;
1546 }
1547 
getErrorKey(LDLErrorType errorType)1548 char* ModelWindow::getErrorKey(LDLErrorType errorType)
1549 {
1550 	static char key[128];
1551 
1552 	sprintf(key, "%s/LDLError%02d", SHOW_ERRORS_KEY, errorType);
1553 	return key;
1554 }
1555 
doErrorClick(int controlId,HWND)1556 BOOL ModelWindow::doErrorClick(int controlId, HWND /*controlHWnd*/)
1557 {
1558 	switch (controlId)
1559 	{
1560 	case IDCANCEL:
1561 		return doErrorOK();
1562 		break;
1563 	case IDC_COPY_ERROR:
1564 		if (!doErrorTreeCopy())
1565 		{
1566 			MessageBeep(MB_OK);
1567 		}
1568 		break;
1569 	case IDC_SHOW_WARNINGS:
1570 		bool showWarnings;
1571 
1572 		showWarnings = CUIDialog::buttonGetCheck(hErrorWindow, controlId);
1573 		TCUserDefaults::setBoolForKey(showWarnings, SHOW_WARNINGS_KEY, false);
1574 		clearErrorTree();
1575 		populateErrorTree();
1576 		break;
1577 	case IDC_ERROR_SHOW_ALL:
1578 		return setAllErrorsSelected(true);
1579 		break;
1580 	case IDC_ERROR_SHOW_NONE:
1581 		return setAllErrorsSelected(false);
1582 		break;
1583 	}
1584 	return TRUE;
1585 }
1586 
setAllErrorsSelected(bool selected)1587 BOOL ModelWindow::setAllErrorsSelected(bool selected)
1588 {
1589 	int i;
1590 	int count = errorInfos->getCount();
1591 	BOOL state = selected ? TRUE : FALSE;
1592 
1593 	skipErrorUpdates = true;
1594 	for (i = 0; i < count; i++)
1595 	{
1596 		ListView_SetCheckState(hErrorList, i, state);
1597 	}
1598 	skipErrorUpdates = false;
1599 	clearErrorTree();
1600 	populateErrorTree();
1601 	return TRUE;
1602 }
1603 
doPageSetupClick(int controlId,HWND)1604 BOOL ModelWindow::doPageSetupClick(int controlId, HWND /*controlHWnd*/)
1605 {
1606 	switch (controlId)
1607 	{
1608 	case IDC_PRINT_BACKGROUND:
1609 		printBackground = CUIDialog::buttonGetCheck(hPageSetupDialog,
1610 			IDC_PRINT_BACKGROUND);
1611 		break;
1612 	default:
1613 		return FALSE;
1614 	}
1615 	return TRUE;
1616 }
1617 
applyPrefs(void)1618 void ModelWindow::applyPrefs(void)
1619 {
1620 	bool antialiasChanged = TCUserDefaults::longForKey(FSAA_MODE_KEY) !=
1621 		currentAntialiasType;
1622 
1623 	loadSettings();
1624 	((LDViewWindow*)parentWindow)->applyPrefs();
1625 	if (LDVExtensionsSetup::haveMultisampleExtension() && antialiasChanged)
1626 	{
1627 		setTimer(FSAA_UPDATE_TIMER, 0);
1628 	}
1629 	forceRedraw();
1630 }
1631 
doCommand(int,int notifyCode,HWND controlHWnd)1632 LRESULT ModelWindow::doCommand(int /*itemId*/, int notifyCode, HWND controlHWnd)
1633 {
1634 	if (controlHWnd == hPrefsWindow && controlHWnd != NULL)
1635 	{
1636 		switch (notifyCode)
1637 		{
1638 		case CUI_OK:
1639 			stopAnimation();
1640 			prefs->closePropertySheet();
1641 			hPrefsWindow = NULL;
1642 			applyPrefs();
1643 			break;
1644 		case CUI_CANCEL:
1645 			stopAnimation();
1646 			prefs->closePropertySheet();
1647 			hPrefsWindow = NULL;
1648 			break;
1649 		case CUI_APPLY:
1650 			applyPrefs();
1651 			break;
1652 		}
1653 	}
1654 	return 1;
1655 }
1656 
doDialogHelp(HWND hDlg,LPHELPINFO helpInfo)1657 BOOL ModelWindow::doDialogHelp(HWND hDlg, LPHELPINFO helpInfo)
1658 {
1659 	BOOL retValue = FALSE;
1660 	DWORD dialogId = 0;
1661 
1662 	if (hDlg == hSaveDialog)
1663 	{
1664 		dialogId = IDD_SAVE_OPTIONS;
1665 	}
1666 	if (dialogId)
1667 	{
1668 		UCSTR helpPath = LDViewPreferences::getLDViewPath(
1669 			ls(_UC("LDView.hlp")));
1670 		DWORD helpId;
1671 
1672 		helpId = 0x80000000 | (dialogId << 16) | (DWORD)helpInfo->iCtrlId;
1673 		WinHelp((HWND)helpInfo->hItemHandle, helpPath, HELP_CONTEXTPOPUP,
1674 			helpId);
1675 		retValue = TRUE;
1676 		delete helpPath;
1677 	}
1678 	return retValue;
1679 }
1680 
doDialogCommand(HWND hDlg,int controlId,int notifyCode,HWND controlHWnd)1681 BOOL ModelWindow::doDialogCommand(HWND hDlg, int controlId, int notifyCode,
1682 								  HWND controlHWnd)
1683 {
1684 //	debugPrintf("ModelWindow::doDialogCommand(0x%08X, 0x%04X, 0x%04X, 0x%08X)\n",
1685 //		hDlg, controlId, notifyCode, controlHWnd);
1686 	if (hDlg == hSaveDialog)
1687 	{
1688 		return doSaveCommand(controlId, notifyCode, controlHWnd);
1689 	}
1690 	else if (hDlg == hPrintDialog)
1691 	{
1692 		return doPrintCommand(controlId, notifyCode, controlHWnd);
1693 	}
1694 	if (notifyCode == BN_CLICKED)
1695 	{
1696 		if (hDlg == hProgressWindow)
1697 		{
1698 			return doProgressClick(controlId, controlHWnd);
1699 		}
1700 		else if (hDlg == hErrorWindow)
1701 		{
1702 			return doErrorClick(controlId, controlHWnd);
1703 		}
1704 		else if (hDlg == hPageSetupDialog)
1705 		{
1706 			return doPageSetupClick(controlId, controlHWnd);
1707 		}
1708 	}
1709 	return FALSE;
1710 }
1711 
showPreferences(void)1712 BOOL ModelWindow::showPreferences(void)
1713 {
1714 	prefs->setHDlgParent(GetParent(hWindow));
1715 	hPrefsWindow = (HWND)prefs->show();
1716 	return TRUE;
1717 }
1718 
showErrors(void)1719 void ModelWindow::showErrors(void)
1720 {
1721 	showErrorsIfNeeded(FALSE);
1722 }
1723 
hideProgress(void)1724 void ModelWindow::hideProgress(void)
1725 {
1726 	if (loading)
1727 	{
1728 		progressBarSetPos(hProgressBar, 0);
1729 		statusBarSetText(hStatusBar, 1, _UC(""));
1730 		((LDViewWindow *)parentWindow)->redrawStatusBar();
1731 		EnumThreadWindows(GetWindowThreadProcessId(hParentWindow, NULL),
1732 			enableNonModalWindow, (LPARAM)hParentWindow);
1733 		((LDViewWindow*)parentWindow)->setLoading(false);
1734 //		doDialogClose(hProgressWindow);
1735 		loading = false;
1736 		((LDViewWindow*)parentWindow)->forceShowStatusBar(false);
1737 	}
1738 }
1739 
addErrorLine(HTREEITEM parent,CUCSTR line,LDLError * error,int imageIndex)1740 HTREEITEM ModelWindow::addErrorLine(HTREEITEM parent, CUCSTR line,
1741 									LDLError* error, int imageIndex)
1742 {
1743 	TVINSERTSTRUCT insertStruct;
1744 	TVITEMEX item;
1745 	ucstring lineCopy = line;
1746 
1747 	stripCRLF(&lineCopy[0]);
1748 	memset(&item, 0, sizeof(item));
1749 	item.mask = TVIF_TEXT | TVIF_PARAM;
1750 	item.pszText = &lineCopy[0];
1751 	item.lParam = (LPARAM)error;
1752 	insertStruct.hParent = parent;
1753 	insertStruct.hInsertAfter = TVI_LAST;
1754 	if (error->getLevel() != LDLAWarning && parent == NULL)
1755 	{
1756 		item.mask |= TVIF_STATE;
1757 		item.stateMask = TVIS_BOLD;
1758 		item.state = TVIS_BOLD;
1759 	}
1760 	if (imageIndex < 0)
1761 	{
1762 		imageIndex = errorImageIndices[LDLELastError + 1];
1763 	}
1764     item.mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
1765     item.iImage = imageIndex;
1766     item.iSelectedImage = imageIndex;
1767 	insertStruct.itemex = item;
1768 	return TreeView_InsertItem(hErrorTree, &insertStruct);
1769 }
1770 
addError(LDLError * error)1771 bool ModelWindow::addError(LDLError* error)
1772 {
1773 	CUCSTR string;
1774 	HTREEITEM parent;
1775 
1776 	if (!showsError(error))
1777 	{
1778 		return false;
1779 	}
1780 	string = error->getMessageUC();
1781 	if (!string)
1782 	{
1783 		string = _UC("");
1784 	}
1785 	parent = addErrorLine(NULL, string, error,
1786 		errorImageIndices[error->getType()]);
1787 
1788 	if (parent)
1789 	{
1790 		TCStringArray *extraInfo;
1791 		ucstring filename;
1792 		utf8toucstring(filename, error->getFilename());
1793 		ucstring line;
1794 
1795 		if (!filename.empty())
1796 		{
1797 			line = ls(_UC("ErrorTreeFilePrefix"));
1798 			line += filename;
1799 		}
1800 		else
1801 		{
1802 			line = ls(_UC("ErrorTreeUnknownFile"));
1803 		}
1804 		addErrorLine(parent, line.c_str(), error);
1805 		ucstring formattedLine;
1806 		utf8toucstring(formattedLine, error->getFormattedFileLine());
1807 		if (!formattedLine.empty())
1808 		{
1809 			int lineNumber = error->getLineNumber();
1810 			if (lineNumber > 0)
1811 			{
1812 				CUCSTR lineNumberFormat = ls(_UC("ErrorTreeLine#"));
1813 				size_t len = ucstrlen(lineNumberFormat) + 128;
1814 				line.resize(len);
1815 				sucprintf(&line[0], len, lineNumberFormat, lineNumber);
1816 				addErrorLine(parent, line.c_str(), error);
1817 			}
1818 			else
1819 			{
1820 				addErrorLine(parent, ls(_UC("ErrorTreeUnknownLine#")), error);
1821 			}
1822 			CUCSTR lineFormat = ls(_UC("ErrorTreeLine"));
1823 			size_t len = formattedLine.size() + ucstrlen(lineFormat) + 1;
1824 			line.resize(len);
1825 			sucprintf(&line[0], len, lineFormat, formattedLine.c_str());
1826 			addErrorLine(parent, line.c_str(), error);
1827 		}
1828 		else
1829 		{
1830 			addErrorLine(parent, ls(_UC("ErrorTreeUnknownLine")), error);
1831 		}
1832 		if ((extraInfo = error->getExtraInfo()) != NULL)
1833 		{
1834 			int i;
1835 			int count = extraInfo->getCount();
1836 
1837 			for (i = 0; i < count; i++)
1838 			{
1839 				ucstring extraLine;
1840 				utf8toucstring(extraLine, extraInfo->stringAtIndex(i));
1841 				addErrorLine(parent, extraLine.c_str(), error);
1842 			}
1843 		}
1844 	}
1845 	return true;
1846 }
1847 
showsError(LDLError * error)1848 bool ModelWindow::showsError(LDLError *error)
1849 {
1850 	LDLErrorType errorType = error->getType();
1851 
1852 	if (error->getLevel() == LDLAWarning)
1853 	{
1854 		if (TCUserDefaults::boolForKey(SHOW_WARNINGS_KEY, false, false))
1855 		{
1856 			return TCUserDefaults::longForKey(getErrorKey(errorType), 0, false)
1857 				!= 0;
1858 		}
1859 		else
1860 		{
1861 			return false;
1862 		}
1863 	}
1864 	else
1865 	{
1866 		return TCUserDefaults::longForKey(getErrorKey(errorType), 1, false) !=
1867 			0;
1868 	}
1869 }
1870 
showsErrorType(LDLErrorType errorType)1871 BOOL ModelWindow::showsErrorType(LDLErrorType errorType)
1872 {
1873 	return TCUserDefaults::longForKey(getErrorKey(errorType), 1, false);
1874 }
1875 
populateErrorInfos(void)1876 void ModelWindow::populateErrorInfos(void)
1877 {
1878 	if (!errorInfos)
1879 	{
1880 		int i;
1881 
1882 		errorInfos = new ErrorInfoArray;
1883 		for (i = LDLEFirstError; i <= LDLELastError; i++)
1884 		{
1885 			ErrorInfo *errorInfo = new ErrorInfo;
1886 			LDLErrorType type = (LDLErrorType)i;
1887 
1888 			errorInfo->setType(type);
1889 			errorInfo->setTypeName(LDLError::getTypeNameUC(type));
1890 			errorInfos->addObject(errorInfo);
1891 			errorInfo->release();
1892 		}
1893 	}
1894 }
1895 
setupErrorWindow(void)1896 void ModelWindow::setupErrorWindow(void)
1897 {
1898 	HIMAGELIST himl;  // handle to image list
1899 	HBITMAP hbmp;     // handle to bitmap
1900 	HBITMAP hMask;
1901 	bool showWarnings = TCUserDefaults::boolForKey(SHOW_WARNINGS_KEY, false,
1902 		false);
1903 
1904 	populateErrorInfos();
1905 	populateErrorList();
1906 	memset(errorImageIndices, 0, sizeof(errorImageIndices));
1907 	if (errorWindowResizer)
1908 	{
1909 		errorWindowResizer->release();
1910 	}
1911 	errorWindowResizer = new CUIWindowResizer;
1912 	errorWindowResizer->setHWindow(hErrorWindow);
1913 	errorWindowResizer->addSubWindow(IDC_ERROR_TREE,
1914 		CUISizeHorizontal | CUISizeVertical);
1915 	errorWindowResizer->addSubWindow(IDC_COPY_ERROR,
1916 		CUIFloatLeft | CUIFloatTop);
1917 	errorWindowResizer->addSubWindow(IDC_ERROR_SHOW_ALL,
1918 		CUIFloatLeft | CUIFloatTop);
1919 	errorWindowResizer->addSubWindow(IDC_ERROR_SHOW_NONE,
1920 		CUIFloatLeft | CUIFloatTop);
1921 	errorWindowResizer->addSubWindow(IDC_SHOW_WARNINGS, CUIFloatTop);
1922 	errorWindowResizer->addSubWindow(IDC_SHOW_ERRORS,
1923 		CUIFloatLeft | CUISizeVertical);
1924 	errorWindowResizer->addSubWindow(IDC_ERROR_LIST,
1925 		CUIFloatLeft | CUISizeVertical);
1926 
1927 	CUIDialog::buttonSetChecked(hErrorWindow, IDC_SHOW_WARNINGS, showWarnings);
1928 	// Create the image list.
1929 	UINT flags = CUIScaler::imageListCreateFlags();
1930 	double scaleFactor = getScaleFactor();
1931 	SIZE size;
1932 	size.cx = size.cy = scalePoints(16);
1933 	if ((himl = ImageList_Create(size.cx, size.cy, flags, 12, 0)) == NULL)
1934 		return;
1935 
1936 	// Add the bitmaps.
1937 	addImageToImageList(himl, IDR_INFO, size, scaleFactor);
1938 	errorImageIndices[LDLEParse] = addImageToImageList(himl, IDR_PARSE, size, scaleFactor);
1939 	errorImageIndices[LDLEGeneral] = errorImageIndices[LDLEParse];
1940 	errorImageIndices[LDLEBFCError] = errorImageIndices[LDLEParse];
1941 	errorImageIndices[LDLEMPDError] = errorImageIndices[LDLEParse];
1942 	errorImageIndices[LDLEMetaCommand] = errorImageIndices[LDLEParse];
1943 	errorImageIndices[LDLEFileNotFound] = addImageToImageList(himl, IDR_FNF, size, scaleFactor);
1944 	errorImageIndices[LDLEMatrix] = addImageToImageList(himl, IDR_MATRIX, size, scaleFactor);
1945 	errorImageIndices[LDLEPartDeterminant] = addImageToImageList(himl, IDR_DETERMINANT, size, scaleFactor);
1946 	errorImageIndices[LDLENonFlatQuad] = addImageToImageList(himl, IDR_NON_FLAT_QUAD, size, scaleFactor);
1947 	errorImageIndices[LDLEConcaveQuad] = addImageToImageList(himl, IDR_CONCAVE_QUAD, size, scaleFactor);
1948 	errorImageIndices[LDLEMatchingPoints] = addImageToImageList(himl, IDR_MATCHING_POINTS, size, scaleFactor);
1949 	errorImageIndices[LDLEColinear] = addImageToImageList(himl, IDR_COLINEAR, size, scaleFactor);
1950 	errorImageIndices[LDLEVertexOrder] = addImageToImageList(himl, IDR_VERTEX_ORDER, size, scaleFactor);
1951 	errorImageIndices[LDLEModelLoop] = addImageToImageList(himl, IDR_ERROR_LOOP, size, scaleFactor);
1952 	if (scaleFactor == 1.0 || scaleFactor == 2.0)
1953 	{
1954 		errorImageIndices[LDLELastError + 1] = addImageToImageList(himl, IDR_DOTS, size, scaleFactor);
1955 	}
1956 	else
1957 	{
1958 		// The dots image will only look right and line up perfectly if the
1959 		// scale factor is exactly 1 or 2. Do what I used to do (show the info
1960 		// icon) if the scale factor is anything else.
1961 		errorImageIndices[LDLELastError + 1] = addImageToImageList(himl, IDR_INFO, size, scaleFactor);
1962 	}
1963 
1964 	// Associate the image list with the tree view control.
1965 	TreeView_SetImageList(hErrorTree, himl, TVSIL_NORMAL);
1966 	TreeView_SetItemHeight(hErrorTree, scalePoints(18));
1967 }
1968 
setupProgress(void)1969 void ModelWindow::setupProgress(void)
1970 {
1971 	cancelLoad = false;
1972 }
1973 
registerErrorWindowClass(void)1974 void ModelWindow::registerErrorWindowClass(void)
1975 {
1976 	WNDCLASSEX windowClass;
1977 	UCCHAR prefsClassName[1024];
1978 
1979 	if (!hProgressWindow)
1980 	{
1981 		createProgress();
1982 	}
1983 	GetClassName(hProgressWindow, prefsClassName, 1024);
1984 	memset(&windowClass, 0, sizeof(windowClass));
1985 	windowClass.cbSize = sizeof(windowClass);
1986 	GetClassInfoEx(getLanguageModule(), prefsClassName, &windowClass);
1987 	windowClass.hIcon = LoadIcon(getLanguageModule(),
1988 		MAKEINTRESOURCE(IDI_APP_ICON));
1989 	windowClass.lpszMenuName = NULL;
1990 	windowClass.lpszClassName = _UC("LDViewErrorWindow");
1991 	RegisterClassEx(&windowClass);
1992 }
1993 
initCommonControls(DWORD mask)1994 void ModelWindow::initCommonControls(DWORD mask)
1995 {
1996 	static DWORD initializedMask = 0;
1997 
1998 	if ((initializedMask & mask) != mask)
1999 	{
2000 		INITCOMMONCONTROLSEX initCtrls;
2001 
2002 		memset(&initCtrls, 0, sizeof(initCtrls));
2003 		initCtrls.dwSize = sizeof(INITCOMMONCONTROLSEX);
2004 		initCtrls.dwICC = mask;
2005 		InitCommonControlsEx(&initCtrls);
2006 		initializedMask |= mask;
2007 	}
2008 }
2009 
createErrorWindow(void)2010 void ModelWindow::createErrorWindow(void)
2011 {
2012 	if (!hErrorWindow)
2013 	{
2014 		HWND hActiveWindow = GetActiveWindow();
2015 		int parts[] = {-1};
2016 
2017 		initCommonControls(ICC_TREEVIEW_CLASSES | ICC_BAR_CLASSES);
2018 		registerErrorWindowClass();
2019 		hErrorWindow = createDialog(IDD_ERRORS, FALSE);
2020 		hErrorStatusWindow = CreateStatusWindow(
2021 			WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, _UC(""), hErrorWindow,
2022 			2000);
2023 		statusBarSetParts(hErrorStatusWindow, 1, parts);
2024 		originalErrorDlgProc = (WNDPROC)GetWindowLongPtr(hErrorWindow,
2025 			DWLP_DLGPROC);
2026 		SetWindowLongPtr(hErrorWindow, GWLP_USERDATA, (LONG_PTR)this);
2027 		SetWindowLongPtr(hErrorWindow, DWLP_DLGPROC, (LONG_PTR)staticErrorDlgProc);
2028 		hErrorTree = GetDlgItem(hErrorWindow, IDC_ERROR_TREE);
2029 		hErrorList = GetDlgItem(hErrorWindow, IDC_ERROR_LIST);
2030 //		hErrorOk = GetDlgItem(hErrorWindow, IDOK);
2031 		setupErrorWindow();
2032 		SetActiveWindow(hActiveWindow);
2033 	}
2034 }
2035 
createProgress(void)2036 void ModelWindow::createProgress(void)
2037 {
2038 	initCommonControls(ICC_PROGRESS_CLASS | ICC_UPDOWN_CLASS);
2039 	hProgressWindow = createDialog(IDD_LOAD_PROGRESS);
2040 	hProgressMessage = GetDlgItem(hProgressWindow, IDC_LOAD_PROGRESS_MSG);
2041 	hProgressCancelButton = GetDlgItem(hProgressWindow, IDCANCEL);
2042 	hProgress = GetDlgItem(hProgressWindow, IDC_PROGRESS);
2043 }
2044 
clearErrorTree(void)2045 void ModelWindow::clearErrorTree(void)
2046 {
2047 //	RECT rect;
2048 //	POINT topLeft;
2049 
2050 	SetWindowRedraw(hErrorTree, FALSE);
2051 	if (hErrorWindow)
2052 	{
2053 		HTREEITEM hItem;
2054 
2055 		// Delete all is really, REALLY, slow for trees with lots of data.  I
2056 		// think it starts at the beginning, and keeps copying the items back
2057 		// each time the first is deleted.  Oh, and the "Visible" in
2058 		// "LastVisible" doesn't mean it's visible on-screen, just that its
2059 		// parent is expanded.
2060 		while ((hItem = TreeView_GetLastVisible(hErrorTree)) != NULL)
2061 		{
2062 			TreeView_DeleteItem(hErrorTree, hItem);
2063 		}
2064 		TreeView_DeleteItem(hErrorTree, TVI_ROOT);
2065 	}
2066 	SetWindowRedraw(hErrorTree, TRUE);
2067 	RedrawWindow(hErrorTree, NULL, NULL,
2068 		RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
2069 /*
2070 	GetWindowRect(hErrorTree, &rect);
2071 	topLeft.x = rect.left;
2072 	topLeft.y = rect.top;
2073 	width = rect.right - rect.left;
2074 	height = rect.bottom - rect.top;
2075 	ScreenToClient(GetParent(hErrorTree), &topLeft);
2076 //	MoveWindow(hErrorTree, topLeft.x, topLeft.y, width - 1, height, TRUE);
2077 	MoveWindow(hErrorTree, topLeft.x, topLeft.y, width, height, TRUE);
2078 */
2079 	errorTreePopulated = false;
2080 }
2081 
clearErrors(void)2082 void ModelWindow::clearErrors(void)
2083 {
2084 	userLoad = true;
2085 	errors->removeAll();
2086 /*
2087 	while (errors->getCount())
2088 	{
2089 		errors->removeObjectAtIndex(0);
2090 	}
2091 */
2092 	clearErrorTree();
2093 }
2094 
populateErrorList(void)2095 void ModelWindow::populateErrorList(void)
2096 {
2097 	int i;
2098 	int count = errorInfos->getCount();
2099 	LVCOLUMN column;
2100 	DWORD exStyle = ListView_GetExtendedListViewStyle(hErrorList);
2101 
2102 	skipErrorUpdates = true;
2103 	exStyle |= LVS_EX_CHECKBOXES;
2104 	ListView_SetExtendedListViewStyle(hErrorList, exStyle);
2105 	memset(&column, 0, sizeof(LVCOLUMN));
2106 	column.mask = LVCF_FMT | LVCF_WIDTH;
2107 	column.fmt = LVCFMT_LEFT;
2108 	column.cx = 300;
2109 	ListView_InsertColumn(hErrorList, 0, &column);
2110 	for (i = 0; i < count; i++)
2111 	{
2112 		ErrorInfo *errorInfo = (*errorInfos)[i];
2113 		LVITEM item;
2114 		int state;
2115 
2116 		memset(&item, 0, sizeof(item));
2117 		item.mask = TVIF_TEXT | TVIF_PARAM;
2118 		item.pszText = errorInfo->getTypeName();
2119 		item.lParam = errorInfo->getType();
2120 		item.iItem = i;
2121 		item.iSubItem = 0;
2122 		state = TCUserDefaults::longForKey(getErrorKey(errorInfo->getType()),
2123 			1, false);
2124 		ListView_InsertItem(hErrorList, &item);
2125 		ListView_SetCheckState(hErrorList, i, state);
2126 	}
2127 	skipErrorUpdates = false;
2128 	ListView_SetColumnWidth(hErrorList, 0, LVSCW_AUTOSIZE);
2129 }
2130 
populateErrorTree(void)2131 int ModelWindow::populateErrorTree(void)
2132 {
2133 	UCCHAR buf[1024] = _UC("");
2134 
2135 	if (!windowShown)
2136 	{
2137 		return 0;
2138 	}
2139 	// De-select any item that may have been selected before.
2140 	TreeView_Select(hErrorTree, NULL, TVGN_CARET);
2141 	// Don't let the tree redraw while we are populating it.
2142 	SetWindowRedraw(hErrorTree, FALSE);
2143 	if (hErrorWindow && !errorTreePopulated)
2144 	{
2145 		errorCount = 0;
2146 		warningCount = 0;
2147 		for (int i = 0, count = errors->getCount(); i < count; i++)
2148 		{
2149 			LDLError *error = (*errors)[i];
2150 
2151 			if (addError(error))
2152 			{
2153 				if (error->getLevel() == LDLAWarning)
2154 				{
2155 					warningCount++;
2156 				}
2157 				else
2158 				{
2159 					errorCount++;
2160 				}
2161 			}
2162 		}
2163 		errorTreePopulated = true;
2164 	}
2165 	SetWindowRedraw(hErrorTree, TRUE);
2166 	RedrawWindow(hErrorTree, NULL, NULL,
2167 		RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
2168 /*
2169 	GetWindowRect(hErrorTree, &rect);
2170 	topLeft.x = rect.left;
2171 	topLeft.y = rect.top;
2172 	width = rect.right - rect.left;
2173 	height = rect.bottom - rect.top;
2174 	ScreenToClient(GetParent(hErrorTree), &topLeft);
2175 	if (errorCount > 0 || warningCount > 0)
2176 	{
2177 		MoveWindow(hErrorTree, topLeft.x, topLeft.y, width - 1, height, FALSE);
2178 	}
2179 	MoveWindow(hErrorTree, topLeft.x, topLeft.y, width, height, TRUE);
2180 */
2181 //	RedrawWindow(hErrorTree, NULL, NULL, RDW_INVALIDATE | RDW_ERASE |
2182 //		RDW_FRAME | RDW_ALLCHILDREN);
2183 	if (errorCount > 0)
2184 	{
2185 		if (errorCount == 1)
2186 		{
2187 			ucstrcpy(buf, ls(_UC("ErrorTreeOneError")));
2188 		}
2189 		else
2190 		{
2191 			sucprintf(buf, COUNT_OF(buf), ls(_UC("ErrorTreeNErrors")), errorCount);
2192 		}
2193 		if (warningCount > 0)
2194 		{
2195 			ucstrcat(buf, _UC(", "));
2196 		}
2197 	}
2198 	if (warningCount > 0)
2199 	{
2200 		if (warningCount == 1)
2201 		{
2202 			ucstrcat(buf, ls(_UC("ErrorTreeOneWarning")));
2203 		}
2204 		else
2205 		{
2206 			size_t len = ucstrlen(buf);
2207 			sucprintf(buf + len, COUNT_OF(buf) - len,
2208 				ls(_UC("ErrorTreeNWarnings")), warningCount);
2209 		}
2210 	}
2211 	statusBarSetText(hErrorStatusWindow, 0, buf);
2212 	return errorCount;
2213 }
2214 
showErrorsIfNeeded(BOOL onlyIfNeeded)2215 void ModelWindow::showErrorsIfNeeded(BOOL onlyIfNeeded)
2216 {
2217 	if (windowShown && !((LDViewWindow*)parentWindow)->getFullScreen())
2218 	{
2219 		if (!hErrorWindow)
2220 		{
2221 			createErrorWindow();
2222 		}
2223 		if (hErrorWindow)
2224 		{
2225 			wchar_t text[1024];
2226 
2227 			populateErrorTree();
2228 			GetWindowTextW(hErrorWindow, text, 1024);
2229 			if (!onlyIfNeeded || (errorCount &&
2230 				TCUserDefaults::longForKey(SHOW_ERRORS_KEY, 1, false)))
2231 			{
2232 				ShowWindow(hErrorWindow, SW_SHOWNORMAL);
2233 				// For some reason, in Windows 10 (and possibly earlier
2234 				// versions), the error window doesn't get brought to
2235 				// the front when it is already visible and ShowWindow is
2236 				// called, even with SW_SHOWNORMAL. The below brings it to the
2237 				// front, so it can't be hidden behind the main LDView window.
2238 				BringWindowToTop(hErrorWindow);
2239 			}
2240 		}
2241 	}
2242 }
2243 
isErrorWindowVisible(void) const2244 bool ModelWindow::isErrorWindowVisible(void) const
2245 {
2246 	return hErrorWindow != NULL && IsWindowVisible(hErrorWindow) != FALSE;
2247 }
2248 
stopAnimation(void)2249 void ModelWindow::stopAnimation(void)
2250 {
2251 	if (modelViewer)
2252 	{
2253 		modelViewer->setRotationSpeed(0.0f);
2254 		modelViewer->setZoomSpeed(0.0f);
2255 	}
2256 }
2257 
loadModel(void)2258 int ModelWindow::loadModel(void)
2259 {
2260 	char* filename = modelViewer->getFilename();
2261 #ifdef _DEBUG
2262 	_CrtMemState ms1, ms2, ms3;
2263 #endif // _DEBUG
2264 
2265 	loadCanceled = false;
2266 	clearErrors();
2267 	stopPolling();
2268 	makeCurrent();
2269 	if (strlen(filename) < 900)
2270 	{
2271 		ucstring ucFilename;
2272 		utf8toucstring(ucFilename, filename);
2273 		ucstring title = _UC("LDView: ") + ucFilename;
2274 		parentWindow->setTitle(title.c_str());
2275 	}
2276 	else
2277 	{
2278 		parentWindow->setTitle(_UC("LDView"));
2279 	}
2280 	_CrtMemCheckpoint(&ms1);
2281 	if (modelViewer->loadModel())
2282 	{
2283 		_CrtMemCheckpoint(&ms2);
2284 		_CrtMemDifference(&ms3, &ms1, &ms2);
2285 		_CrtMemDumpStatistics(&ms3);
2286 		forceRedraw();
2287 		stopAnimation();
2288 		if (pollSetting)
2289 		{
2290 			if (getFileTime(&lastWriteTime))
2291 			{
2292 				startPolling();
2293 			}
2294 		}
2295 		return 1;
2296 	}
2297 	else
2298 	{
2299 		modelViewer->setFilename(NULL);
2300 		return 0;
2301 	}
2302 }
2303 
closeWindow(void)2304 void ModelWindow::closeWindow(void)
2305 {
2306 	destroyWindow();
2307 	CUIOGLWindow::closeWindow();
2308 }
2309 
doShowWindow(BOOL showFlag,LPARAM status)2310 LRESULT ModelWindow::doShowWindow(BOOL showFlag, LPARAM status)
2311 {
2312 	windowShown = true;
2313 	return CUIOGLWindow::doShowWindow(showFlag, status);
2314 }
2315 
initWindow(void)2316 BOOL ModelWindow::initWindow(void)
2317 {
2318 	LDVExtensionsSetup::setup(hInstance);
2319 	//if (((LDViewWindow*)parentWindow)->getFullScreen() ||
2320 	//	((LDViewWindow*)parentWindow)->getScreenSaver() ||
2321 	//	((LDViewWindow*)parentWindow)->getHParentWindow())
2322 	//{
2323 	//	exWindowStyle &= ~WS_EX_CLIENTEDGE;
2324 	//}
2325 	//else
2326 	//{
2327 	//	exWindowStyle |= WS_EX_CLIENTEDGE;
2328 	//}
2329 	windowStyle |= WS_CHILD;
2330 	cancelLoad = false;
2331 	if (CUIOGLWindow::initWindow())
2332 	{
2333 		TREGLExtensions::setup();
2334 		setupMultisample();
2335 		return TRUE;
2336 	}
2337 	else
2338 	{
2339 		return FALSE;
2340 	}
2341 }
2342 
setupMultisample(void)2343 void ModelWindow::setupMultisample(void)
2344 {
2345 	if (LDVExtensionsSetup::haveMultisampleExtension())
2346 	{
2347 		debugOut("ModelWindow::setupMultisample 1\n");
2348 		if (currentAntialiasType)
2349 		{
2350 			if (TREGLExtensions::haveNvMultisampleFilterHintExtension())
2351 			{
2352 				if (prefs->getUseNvMultisampleFilter())
2353 				{
2354 					glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
2355 				}
2356 				else
2357 				{
2358 					glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_FASTEST);
2359 				}
2360 			}
2361 			glEnable(GL_MULTISAMPLE_ARB);
2362 		}
2363 		else
2364 		{
2365 			if (TREGLExtensions::haveNvMultisampleFilterHintExtension())
2366 			{
2367 				glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_FASTEST);
2368 			}
2369 			glDisable(GL_MULTISAMPLE_ARB);
2370 		}
2371 		debugOut("ModelWindow::setupMultisample 2\n");
2372 	}
2373 }
2374 
staticErrorDlgProc(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam)2375 LRESULT CALLBACK ModelWindow::staticErrorDlgProc(HWND hDlg, UINT message,
2376 												 WPARAM wParam, LPARAM lParam)
2377 {
2378 	ModelWindow* modelWindow = (ModelWindow*)GetWindowLongPtr(hDlg,
2379 		GWLP_USERDATA);
2380 
2381 	if (modelWindow)
2382 	{
2383 		return modelWindow->errorDlgProc(hDlg, message, wParam, lParam);
2384 	}
2385 	else
2386 	{
2387 		return 0;
2388 	}
2389 }
2390 
errorDlgProc(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam)2391 LRESULT ModelWindow::errorDlgProc(HWND hDlg, UINT message, WPARAM wParam,
2392 								  LPARAM lParam)
2393 {
2394 //	debugPrintf("ModelWindow::errorDlgProc: 0x%04x\n", message);
2395 	return CallWindowProc(originalErrorDlgProc, hDlg, message, wParam, lParam);
2396 }
2397 
windowProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)2398 LRESULT ModelWindow::windowProc(HWND hWnd, UINT message, WPARAM wParam,
2399 							  LPARAM lParam)
2400 {
2401 #if defined(USE_CPP11) || !defined(_NO_BOOST)
2402 	if (remoteListener && remoteMessageID != 0 && message == remoteMessageID)
2403 	{
2404 		processRemoteMessage((char *)lParam);
2405 	}
2406 #endif // !_NO_BOOST
2407 //	debugPrintf("ModelWindow::windowProc: 0x%04x\n", message);
2408 	return CUIOGLWindow::windowProc(hWnd, message, wParam, lParam);
2409 }
2410 
processModalMessage(MSG msg)2411 void ModelWindow::processModalMessage(MSG msg)
2412 {
2413 	if (msg.message == WM_KEYDOWN)
2414 	{
2415 		msg.hwnd = hParentWindow;
2416 	}
2417 	CUIWindow::processModalMessage(msg);
2418 }
2419 
progressCallback(CUCSTR message,float progress,bool fromImage,bool showErrors)2420 int ModelWindow::progressCallback(
2421 	CUCSTR message,
2422 	float progress,
2423 	bool fromImage,
2424 	bool showErrors /*= false*/)
2425 {
2426 	DWORD thisProgressUpdate = GetTickCount();
2427 
2428 	if (!windowShown)
2429 	{
2430 		return 1;
2431 	}
2432 	if (ucstrcmp(message, _UC("LoadingPNG")) == 0 ||
2433 		ucstrcmp(message, _UC("LoadingPNGRow")) == 0)
2434 	{
2435 		// The only time we get this message is when loading built-in PNG files,
2436 		// like toolbar icons and stud logo; don't show it in the progress.
2437 		return 1;
2438 	}
2439 	if (progress == 2.0 && (!userLoad || !fromImage))
2440 	{
2441 		// done
2442 		hideProgress();
2443 		// If the window is closed during load, we will no longer be initialized.
2444 		if (showErrors && initialized && !cancelLoad && userLoad)
2445 		{
2446 			showErrorsIfNeeded();
2447 		}
2448 		if (!fromImage)
2449 		{
2450 			userLoad = false;
2451 		}
2452 		makeCurrent();
2453 		return 1;
2454 	}
2455 	if (!loading)
2456 	{
2457 		//clearErrors();
2458 		loading = true;
2459 		setupProgress();
2460 		((LDViewWindow*)parentWindow)->forceShowStatusBar(true);
2461 		if (!flushModal(hParentWindow, false))
2462 		{
2463 			PostQuitMessage(0);
2464 			cancelLoad = true;
2465 		}
2466 		if (cancelLoad)
2467 		{
2468 			loadCanceled = true;
2469 		}
2470 		((LDViewWindow*)parentWindow)->setLoading(true);
2471 	}
2472 	if (message && message[0])
2473 	{
2474 		setStatusText(hStatusBar, 1, message, true);
2475 	}
2476 	if (progress >= 0.0f)
2477 	{
2478 		int oldProgress;
2479 		int newProgress = (int)(progress * 100);
2480 
2481 		oldProgress = progressBarGetPos(hProgressBar);
2482 		if (oldProgress != newProgress)
2483 		{
2484 			progressBarSetPos(hProgressBar, newProgress);
2485 		}
2486 	}
2487 	if (thisProgressUpdate < lastProgressUpdate || thisProgressUpdate >
2488 		lastProgressUpdate + 100 || progress == 1.0f)
2489 	{
2490 		if (!flushModal(hParentWindow, false))
2491 		{
2492 			PostQuitMessage(0);
2493 			cancelLoad = true;
2494 		}
2495 		if (cancelLoad)
2496 		{
2497 			loadCanceled = true;
2498 		}
2499 		lastProgressUpdate = thisProgressUpdate;
2500 	}
2501 	if (cancelLoad)
2502 	{
2503 		hideProgress();
2504 		makeCurrent();
2505 		return 0;
2506 	}
2507 	else
2508 	{
2509 		makeCurrent();
2510 		return 1;
2511 	}
2512 }
2513 
2514 //bool ModelWindow::staticImageProgressCallback(CUCSTR message, float progress,
2515 //											  void* userData)
2516 //{
2517 //	return ((ModelWindow*)userData)->progressCallback(message, progress) ? true
2518 //		: false;
2519 //}
2520 
errorCallback(LDLError * error)2521 int ModelWindow::errorCallback(LDLError* error)
2522 {
2523 	if (windowShown)
2524 	{
2525 		errors->addObject(error);
2526 	}
2527 	return 1;
2528 }
2529 
doCreate(HWND hWnd,LPCREATESTRUCT lpcs)2530 LRESULT ModelWindow::doCreate(HWND hWnd, LPCREATESTRUCT lpcs)
2531 {
2532 	if (CUIOGLWindow::doCreate(hWnd, lpcs) == -1)
2533 	{
2534 		return -1;
2535 	}
2536 	else
2537 	{
2538 		DragAcceptFiles(hWnd, TRUE);
2539 		return 0;
2540 	}
2541 }
2542 
doDropFiles(HDROP hDrop)2543 LRESULT ModelWindow::doDropFiles(HDROP hDrop)
2544 {
2545 	UCCHAR buf[1024];
2546 	if (DragQueryFile(hDrop, 0, buf, COUNT_OF(buf)) > 0)
2547 	{
2548 		DragFinish(hDrop);
2549 		((LDViewWindow*)parentWindow)->openModel(buf);
2550 		return 0;
2551 	}
2552 	return 1;
2553 }
2554 
checkForPart(void)2555 void ModelWindow::checkForPart(void)
2556 {
2557 	bool isPart = false;
2558 	UCCHAR buf[MAX_PATH];
2559 	UCSTR filePart;
2560 	ucstring filename;
2561 	utf8toucstring(filename, modelViewer->getFilename());
2562 
2563 	if (GetFullPathName(filename.c_str(), COUNT_OF(buf), buf, &filePart))
2564 	{
2565 		char partsDir[1024];
2566 		std::string fullPath;
2567 		ucstringtoutf8(fullPath, buf);
2568 
2569 		*filePart = 0;
2570 		stripTrailingPathSeparators(buf);
2571 		strcpy(partsDir, LDLModel::lDrawDir());
2572 		strcat(partsDir, "\\PARTS");
2573 		convertStringToUpper(partsDir);
2574 		convertStringToUpper(&fullPath[0]);
2575 		replaceStringCharacter(&fullPath[0], '/', '\\');
2576 		replaceStringCharacter(partsDir, '/', '\\');
2577 		if (fullPath == partsDir)
2578 		{
2579 			isPart = true;
2580 		}
2581 		else
2582 		{
2583 			ucstring ucPartsDir;
2584 			utf8toucstring(ucPartsDir, partsDir);
2585 			UCCHAR shortPath[1024];
2586 
2587 			if (GetShortPathName(ucPartsDir.c_str(), shortPath,
2588 				COUNT_OF(shortPath)))
2589 			{
2590 				if (ucstrcmp(buf, shortPath) == 0)
2591 				{
2592 					isPart = true;
2593 				}
2594 			}
2595 		}
2596 	}
2597 	modelViewer->setFileIsPart(isPart);
2598 }
2599 
chDirFromFilename(CUCSTR filename,UCSTR outFilename)2600 bool ModelWindow::chDirFromFilename(CUCSTR filename, UCSTR outFilename)
2601 {
2602 	UCCHAR buf[MAX_PATH];
2603 	UCCHAR* fileSpot;
2604 	DWORD result = GetFullPathName(filename, MAX_PATH, buf, &fileSpot);
2605 
2606 	if (result <= MAX_PATH && result > 0)
2607 	{
2608 //		if (strlen(fileSpot) < strlen(filename))
2609 		{
2610 			ucstrcpy(outFilename, buf);
2611 		}
2612 		*fileSpot = 0;
2613 		if (SetCurrentDirectory(buf))
2614 		{
2615 			return true;
2616 		}
2617 	}
2618 	return false;
2619 }
2620 
printSystemError(void)2621 void ModelWindow::printSystemError(void)
2622 {
2623 #ifdef _DEBUG
2624 	DWORD error = GetLastError();
2625 	UCSTR buf;
2626 
2627 	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
2628 		FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error,
2629 		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (UCSTR)&buf,
2630 		0, NULL);
2631 	_CrtDbgReport(_CRT_WARN, NULL, 0, NULL, "%s\n", buf);
2632 	LocalFree(buf);
2633 #endif
2634 }
2635 
setupMaterial(void)2636 void ModelWindow::setupMaterial(void)
2637 {
2638 	// Don't call super.
2639 }
2640 
setupLighting(void)2641 void ModelWindow::setupLighting(void)
2642 {
2643 //	LDrawModelViewer::setPolygonOffsetFunc((GLPolygonOffsetFunc)glPolygonOffset);
2644 	modelViewer->setup();
2645 }
2646 
setStatusText(HWND hStatus,int part,CUCSTR text,bool redraw)2647 void ModelWindow::setStatusText(
2648 	HWND hStatus,
2649 	int part,
2650 	CUCSTR text,
2651 	bool redraw)
2652 {
2653 	((LDViewWindow*)parentWindow)->setStatusText(hStatus, part, text, redraw);
2654 }
2655 
drawFPS(void)2656 void ModelWindow::drawFPS(void)
2657 {
2658 	if (prefs->getShowsFPS())
2659 	{
2660 		if (((LDViewWindow*)parentWindow)->getFullScreen() || !hStatusBar)
2661 		{
2662 			if (currentAntialiasType)
2663 			{
2664 				glDisable(GL_MULTISAMPLE_ARB);
2665 			}
2666 			modelViewer->drawFPS(fps);
2667 			if (currentAntialiasType)
2668 			{
2669 				glEnable(GL_MULTISAMPLE_ARB);
2670 			}
2671 		}
2672 		else if (hStatusBar)
2673 		{
2674 			UCCHAR fpsString[1024] = _UC("");
2675 
2676 			if (modelViewer->getMainTREModel())
2677 			{
2678 				if (fps > 0.0f)
2679 				{
2680 					sucprintf(fpsString, COUNT_OF(fpsString),
2681 						ls(_UC("FPSFormat")), fps);
2682 				}
2683 				else
2684 				{
2685 					ucstrcpy(fpsString,
2686 						ls(_UC("FPSSpinPrompt")));
2687 				}
2688 			}
2689 			setStatusText(hStatusBar, 1, fpsString);
2690 		}
2691 	}
2692 	else if (hStatusBar)
2693 	{
2694 		setStatusText(hStatusBar, 1, _UC(""));
2695 	}
2696 }
2697 
doPostPaint(void)2698 void ModelWindow::doPostPaint(void)
2699 {
2700 	checkFileForUpdates();
2701 	if (hProgressBar)
2702 	{
2703 		if (!RedrawWindow(hProgressBar, NULL, NULL, RDW_ERASE | RDW_INVALIDATE
2704 			| RDW_ERASENOW | RDW_UPDATENOW | RDW_ALLCHILDREN))
2705 		{
2706 			debugPrintf("RedrawWindow failed!\n");
2707 		}
2708 	}
2709 }
2710 
updateFPS(void)2711 void ModelWindow::updateFPS(void)
2712 {
2713 	DWORD thisFrameTime;
2714 
2715 	thisFrameTime = timeGetTime();
2716 	numFramesSinceReference++;
2717 	if (firstFPSPass)
2718 	{
2719 		fps = 0.0f;
2720 		referenceFrameTime = thisFrameTime;
2721 		numFramesSinceReference = 0;
2722 		firstFPSPass = false;
2723 	}
2724 	else if (thisFrameTime - referenceFrameTime >= 250)
2725 	{
2726 		if (thisFrameTime > referenceFrameTime)
2727 		{
2728 			fps = 1000.0f / (TCFloat)(thisFrameTime - referenceFrameTime) *
2729 				numFramesSinceReference;
2730 		}
2731 		else
2732 		{
2733 			fps = 0.0f;
2734 		}
2735 		referenceFrameTime = thisFrameTime;
2736 		numFramesSinceReference = 0;
2737 		firstFPSPass = false;
2738 	}
2739 	if (!frontBufferFPS())
2740 	{
2741 		drawFPS();
2742 	}
2743 }
2744 
frontBufferFPS(void)2745 bool ModelWindow::frontBufferFPS(void)
2746 {
2747 	if (prefs->getUseNvMultisampleFilter())
2748 	{
2749 		static bool checkedSwapInterval = false;
2750 		static int swapInterval = 0;
2751 
2752 		if (!checkedSwapInterval)
2753 		{
2754 			int (*wglGetSwapIntervalEXT)(void) =
2755 				(int(*)(void))wglGetProcAddress("wglGetSwapIntervalEXT");
2756 
2757 			checkedSwapInterval = true;
2758 			if (wglGetSwapIntervalEXT)
2759 			{
2760 				swapInterval = wglGetSwapIntervalEXT();
2761 			}
2762 			else
2763 			{
2764 				swapInterval = 0;
2765 			}
2766 		}
2767 
2768 		if (swapInterval > 0)
2769 		{
2770 			// We only want to draw the FPS text to the front buffer if vsync
2771 			// is turned on.
2772 			return true;
2773 		}
2774 	}
2775 	return false;
2776 }
2777 
swapBuffers(void)2778 void ModelWindow::swapBuffers(void)
2779 {
2780 	if (!SwapBuffers(hdc))
2781 	{
2782 		DWORD error = GetLastError();
2783 		UCCHAR buf[1024];
2784 
2785 		FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
2786 			FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, 0, buf,
2787 			1024, NULL);
2788 		char *errorMessage = ucstringtombs(buf);
2789 		debugPrintf("swapBuffers error: %s\n", errorMessage);
2790 		delete[] errorMessage;
2791 	}
2792 	if (frontBufferFPS())
2793 	{
2794 		glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_FASTEST);
2795 		glDisable(GL_MULTISAMPLE_ARB);
2796 		glDrawBuffer(GL_FRONT);
2797 		drawFPS();
2798 		glDrawBuffer(GL_BACK);
2799 		glFlush();
2800 		glEnable(GL_MULTISAMPLE_ARB);
2801 		glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
2802 	}
2803 }
2804 
doPaint(void)2805 void ModelWindow::doPaint(void)
2806 {
2807 	bool needsPostRedraw = (modelViewer->getNeedsReload() ||
2808 		modelViewer->getNeedsRecompile()) && modelViewer->getMainTREModel();
2809 
2810 //	debugPrintf("ModelWindow::doPaint\n");
2811 	if (offscreenActive || loading)
2812 	{
2813 		static bool looping = false;
2814 		if (!looping)
2815 		{
2816 			RECT rect;
2817 			POINT points[2];
2818 
2819 			GetClientRect(hWindow, &rect);
2820 			points[0].x = rect.left;
2821 			points[0].y = rect.top;
2822 			points[1].x = rect.right;
2823 			points[1].y = rect.bottom;
2824 			MapWindowPoints(hWindow, hParentWindow, points, 2);
2825 			rect.left = points[0].x;
2826 			rect.top = points[0].y;
2827 			rect.right = points[1].x;
2828 			rect.bottom = points[1].y;
2829 			looping = true;
2830 //			RedrawWindow(hWindow, NULL, NULL, RDW_ERASE/* | RDW_ERASENOW*/);
2831 			RedrawWindow(hParentWindow, &rect, NULL, RDW_ERASE | RDW_INVALIDATE
2832 				| RDW_ERASENOW | RDW_UPDATENOW);
2833 			ValidateRect(hWindow, NULL);
2834 			looping = false;
2835 		}
2836 		else
2837 		{
2838 			ValidateRect(hWindow, NULL);
2839 		}
2840 		return;
2841 	}
2842 	if (!initializedGL)
2843 	{
2844 		forceRedraw();
2845 		return;
2846 	}
2847 	if (redrawCount > 0)
2848 	{
2849 		//debugPrintf("Multi-forced redraw.\n");
2850 		forceRedraw();
2851 		redrawCount--;
2852 		return;
2853 	}
2854 	makeCurrent();
2855 	//if (needsRecompile)
2856 	//{
2857 	//	needsRecompile = false;
2858 	//	recompile();
2859 	//}
2860 	if (loading)
2861 	{
2862 		modelViewer->clear();
2863 		swapBuffers();
2864 		return;
2865 	}
2866 	forceRedraw();
2867 	redrawRequested = false;
2868 	modelViewer->update();
2869 
2870 	//updateSpinRate();
2871 	//if ((fEq(rotationSpeed, 0.0f) && fEq(modelViewer->getZoomSpeed(), 0.0f)
2872 	//	&& fEq(modelViewer->getCameraXRotate(), 0.0f) &&
2873 	//	fEq(modelViewer->getCameraYRotate(), 0.0f) &&
2874 	//	fEq(modelViewer->getCameraZRotate(), 0.0f) &&
2875 	//	fEq(modelViewer->getCameraMotion().length(), 0.0f))
2876 	//	|| modelViewer->getPaused())
2877 	if (!redrawRequested)
2878 	{
2879 		if (redrawCount == 0)
2880 		{
2881 			ValidateRect(hWindow, NULL);
2882 			firstFPSPass = true;
2883 		}
2884 	}
2885 	updateFPS();
2886 	if (redrawCount > 0)
2887 	{
2888 		redrawCount--;
2889 	}
2890 	swapBuffers();
2891 	if (needsPostRedraw)
2892 	{
2893 		forceRedraw();
2894 	}
2895 	((LDViewWindow *)parentWindow)->showStatusLatLon();
2896 }
2897 
doNCDestroy(void)2898 LRESULT ModelWindow::doNCDestroy(void)
2899 {
2900 	if (prefs && !applyingPrefs)
2901 	{
2902 		prefs->closePropertySheet(true);
2903 		hPrefsWindow = NULL;
2904 	}
2905 	if (hProgressWindow)
2906 	{
2907 		DestroyWindow(hProgressWindow);
2908 		hProgressWindow = NULL;
2909 	}
2910 	if (hErrorWindow)
2911 	{
2912 		DestroyWindow(hErrorWindow);
2913 		hErrorWindow = NULL;
2914 	}
2915 	return CUIOGLWindow::doNCDestroy();
2916 }
2917 
doDestroy(void)2918 LRESULT ModelWindow::doDestroy(void)
2919 {
2920 	cancelLoad = true;
2921 	openGlWillEnd();
2922 //	RevokeDragDrop(hWindow);
2923 	return CUIOGLWindow::doDestroy();
2924 }
2925 
zoom(TCFloat amount)2926 void ModelWindow::zoom(TCFloat amount)
2927 {
2928 	if (modelViewer)
2929 	{
2930 		modelViewer->zoom(amount);
2931 		forceRedraw();
2932 	}
2933 }
2934 
setClipZoom(bool value)2935 void ModelWindow::setClipZoom(bool value)
2936 {
2937 	if (modelViewer)
2938 	{
2939 		modelViewer->setClipZoom(value);
2940 	}
2941 }
2942 
getClipZoom(void)2943 bool ModelWindow::getClipZoom(void)
2944 {
2945 	if (modelViewer)
2946 	{
2947 		return modelViewer->getClipZoom();
2948 	}
2949 	else
2950 	{
2951 		return false;
2952 	}
2953 }
2954 
startPolling(void)2955 void ModelWindow::startPolling(void)
2956 {
2957 	if (pollSetting && !pollTimerRunning && !loading)
2958 	{
2959 		setTimer(POLL_TIMER, POLL_INTERVAL);
2960 		lastFileSizeHigh = lastFileSizeLow = 0;
2961 		pollTimerRunning = true;
2962 	}
2963 }
2964 
stopPolling(void)2965 BOOL ModelWindow::stopPolling(void)
2966 {
2967 	if (loading)
2968 	{
2969 		return TRUE;
2970 	}
2971 	else
2972 	{
2973 		pollTimerRunning = false;
2974 		return killTimer(POLL_TIMER);
2975 	}
2976 }
2977 
setPollSetting(int value)2978 void ModelWindow::setPollSetting(int value)
2979 {
2980 	if (value != pollSetting)
2981 	{
2982 		pollSetting = value;
2983 		if (pollSetting)
2984 		{
2985 			startPolling();
2986 		}
2987 		else
2988 		{
2989 			stopPolling();
2990 		}
2991 	}
2992 }
2993 
2994 //bool ModelWindow::writeImage(char *filename, int width, int height,
2995 //							 BYTE *buffer, char *formatName, bool saveAlpha)
2996 //{
2997 //	TCImage *image = new TCImage;
2998 //	bool retValue;
2999 //	const char *version = LDViewWindow::getProductVersion();
3000 //	char comment[1024];
3001 //
3002 //	if (saveAlpha)
3003 //	{
3004 //		image->setDataFormat(TCRgba8);
3005 //	}
3006 //	image->setSize(width, height);
3007 //	image->setLineAlignment(4);
3008 //	image->setImageData(buffer);
3009 //	image->setFormatName(formatName);
3010 //	image->setFlipped(true);
3011 //	if (strcasecmp(formatName, "PNG") == 0)
3012 //	{
3013 //		strcpy(comment, "Software:!:!:LDView");
3014 //	}
3015 //	else
3016 //	{
3017 //		strcpy(comment, "Created by LDView");
3018 //	}
3019 //	if (version)
3020 //	{
3021 //		strcat(comment, " ");
3022 //		strcat(comment, version);
3023 //	}
3024 //	image->setComment(comment);
3025 //	if (TCUserDefaults::longForKey(AUTO_CROP_KEY, 0, false))
3026 //	{
3027 //		image->autoCrop((BYTE)modelViewer->getBackgroundR(),
3028 //			(BYTE)modelViewer->getBackgroundG(),
3029 //			(BYTE)modelViewer->getBackgroundB());
3030 //	}
3031 //	retValue = image->saveFile(filename, staticImageProgressCallback, this);
3032 //	image->release();
3033 //	return retValue;
3034 //}
3035 
3036 //bool ModelWindow::writeBmp(char *filename, int width, int height, BYTE *buffer)
3037 //{
3038 //	return writeImage(filename, width, height, buffer, "BMP");
3039 //}
3040 //
3041 //bool ModelWindow::writePng(char *filename, int width, int height, BYTE *buffer,
3042 //						   bool saveAlpha)
3043 //{
3044 //	return writeImage(filename, width, height, buffer, "PNG", saveAlpha);
3045 //}
3046 
setupBitmapRender(int imageWidth,int imageHeight)3047 bool ModelWindow::setupBitmapRender(int imageWidth, int imageHeight)
3048 {
3049 	hBitmapRenderDC = CreateCompatibleDC(NULL);
3050 	BYTE *bmBuffer;
3051 
3052 	if (!hBitmapRenderDC)
3053 	{
3054 		return false;
3055 	}
3056 	hRenderBitmap = createDIBSection(hBitmapRenderDC, imageWidth, imageHeight,
3057 		&bmBuffer);
3058 	if (hRenderBitmap)
3059 	{
3060 		PIXELFORMATDESCRIPTOR pfd;
3061 		int lpfIndex;
3062 
3063 		memset(&pfd, 0, sizeof(pfd));
3064 		pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
3065 		pfd.nVersion = 1;
3066 		pfd.dwFlags = PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL;
3067 		pfd.iPixelType = PFD_TYPE_RGBA;
3068 		pfd.cColorBits = 24;
3069 		pfd.cDepthBits = 32;
3070 		pfd.cStencilBits = 2;
3071 		pfd.cAlphaBits = 8;
3072 		SelectObject(hBitmapRenderDC, hRenderBitmap);
3073 		lpfIndex = ChoosePixelFormat(hBitmapRenderDC, &pfd);
3074 		if (lpfIndex)
3075 		{
3076 			if (SetPixelFormat(hBitmapRenderDC, lpfIndex, &pfd))
3077 			{
3078 				hBitmapRenderGLRC = wglCreateContext(hBitmapRenderDC);
3079 				if (hBitmapRenderGLRC)
3080 				{
3081 					openGlWillEnd();
3082 					TREGLExtensions::disableAll(true);
3083 					hCurrentDC = hBitmapRenderDC;
3084 					hCurrentGLRC = hBitmapRenderGLRC;
3085 					wglMakeCurrent(hBitmapRenderDC, hBitmapRenderGLRC);
3086 					setupMaterial();
3087 					setupLighting();
3088 					glDepthFunc(GL_LEQUAL);
3089 					glEnable(GL_DEPTH_TEST);
3090 					glDrawBuffer(GL_FRONT);
3091 					glReadBuffer(GL_FRONT);
3092 					modelViewer->setWidth(imageWidth);
3093 					modelViewer->setHeight(imageHeight);
3094 					modelViewer->setup();
3095 					modelViewer->pause();
3096 					modelViewer->setNeedsRecompile();
3097 					return true;
3098 				}
3099 			}
3100 		}
3101 		DeleteObject(hRenderBitmap);
3102 	}
3103 	DeleteDC(hBitmapRenderDC);
3104 	hBitmapRenderDC = NULL;
3105 	return false;
3106 }
3107 
setupOffscreen(int imageWidth,int imageHeight,bool antialias)3108 bool ModelWindow::setupOffscreen(
3109 	int imageWidth,
3110 	int imageHeight,
3111 	bool antialias)
3112 {
3113 	if (snapshotTaker != NULL && snapshotTaker->getUseFBO())
3114 	{
3115 		if (saveImageType == PNG_IMAGE_TYPE_INDEX &&
3116 			TCUserDefaults::boolForKey(HDR_SNAPSHOTS_KEY))
3117 		{
3118 			snapshotTaker->set16BPC(true);
3119 		}
3120 		return true;
3121 	}
3122 	else if (setupPBuffer(imageWidth, imageHeight, antialias))
3123 	{
3124 		return true;
3125 	}
3126 	else
3127 	{
3128 		return setupBitmapRender(imageWidth, imageHeight);
3129 	}
3130 }
3131 
setupPBuffer(int imageWidth,int imageHeight,bool antialias)3132 bool ModelWindow::setupPBuffer(int imageWidth, int imageHeight,
3133 							   bool antialias)
3134 {
3135 	if (LDVExtensionsSetup::havePixelBufferExtension())
3136 	{
3137 		GLint intValues[] = {
3138 			WGL_DRAW_TO_PBUFFER_ARB, GL_TRUE,
3139 			WGL_RED_BITS_ARB, 8,
3140 			WGL_GREEN_BITS_ARB, 8,
3141 			WGL_BLUE_BITS_ARB, 8,
3142 			WGL_ALPHA_BITS_ARB, 8,
3143 			WGL_STENCIL_BITS_ARB, 2,
3144 			0, 0,
3145 			0, 0,
3146 			0, 0
3147 		};
3148 		int index;
3149 
3150 		if (antialias)
3151 		{
3152 			int offset = sizeof(intValues) / sizeof(GLint) - 6;
3153 
3154 			intValues[offset++] = WGL_SAMPLES_EXT;
3155 			intValues[offset++] = prefs->getFSAAFactor();
3156 			intValues[offset++] = WGL_SAMPLE_BUFFERS_EXT;
3157 			intValues[offset++] = GL_TRUE;
3158 		}
3159 		index = LDVExtensionsSetup::choosePixelFormat(hdc, intValues);
3160 		if (index >= 0)
3161 		{
3162 			using namespace TREGLExtensionsNS;
3163 
3164 			if (wglCreatePbufferARB && wglGetPbufferDCARB &&
3165 				wglGetPixelFormatAttribivARB)
3166 			{
3167 				int pfSizeAttribs[3] = {
3168 					WGL_MAX_PBUFFER_WIDTH_ARB,
3169 					WGL_MAX_PBUFFER_HEIGHT_ARB,
3170 					WGL_MAX_PBUFFER_PIXELS_ARB
3171 				};
3172 				int attribValues[3];
3173 
3174 				wglGetPixelFormatAttribivARB(hdc, index, 0, 3, pfSizeAttribs,
3175 					attribValues);
3176 				// This shouldn't be necessary, but ATI returns a PBuffer even
3177 				// if we ask for one that is too big, so we can't rely on their
3178 				// failure to trigger failure.  The one it returns CLAIMS to be
3179 				// the size we asked for; it just doesn't work right.
3180 				if (attribValues[0] >= imageWidth &&
3181 					attribValues[1] >= imageHeight &&
3182 					attribValues[2] >= imageWidth * imageHeight)
3183 				{
3184 					// Given the above check, the following shouldn't really
3185 					// matter, but I'll leave it in anyway.
3186 					GLint cbpIntValues[] = {
3187 						WGL_PBUFFER_LARGEST_ARB, 0,
3188 						0, 0
3189 					};
3190 					hPBuffer = wglCreatePbufferARB(hdc, index, imageWidth,
3191 						imageHeight, cbpIntValues);
3192 
3193 					if (hPBuffer)
3194 					{
3195 						hPBufferDC = wglGetPbufferDCARB(hPBuffer);
3196 						if (hPBufferDC)
3197 						{
3198 							hPBufferGLRC = wglCreateContext(hPBufferDC);
3199 
3200 							if (hPBufferGLRC)
3201 							{
3202 								wglShareLists(hglrc, hPBufferGLRC);
3203 								hCurrentDC = hPBufferDC;
3204 								hCurrentGLRC = hPBufferGLRC;
3205 								makeCurrent();
3206 								if (antialias)
3207 								{
3208 									setupMultisample();
3209 								}
3210 								setupMaterial();
3211 								setupLighting();
3212 								glDepthFunc(GL_LEQUAL);
3213 								glEnable(GL_DEPTH_TEST);
3214 								glDrawBuffer(GL_FRONT);
3215 								glReadBuffer(GL_FRONT);
3216 								modelViewer->setWidth(imageWidth);
3217 								modelViewer->setHeight(imageHeight);
3218 								modelViewer->setup();
3219 								modelViewer->pause();
3220 								if (modelViewer->getNeedsReload())
3221 								{
3222 									return true;
3223 								}
3224 								else
3225 								{
3226 									// No need to recompile as before, because
3227 									// we're sharing display lists.
3228 									return true;
3229 								}
3230 							}
3231 						}
3232 					}
3233 				}
3234 			}
3235 		}
3236 		cleanupOffscreen();
3237 		if (antialias)
3238 		{
3239 			return setupPBuffer(imageWidth, imageHeight, false);
3240 		}
3241 	}
3242 	return false;
3243 }
3244 
renderOffscreenImage(void)3245 void ModelWindow::renderOffscreenImage(void)
3246 {
3247 	makeCurrent();
3248 	modelViewer->update();
3249 	//if (canSaveAlpha())
3250 	//{
3251 	//	glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT |
3252 	//		GL_VIEWPORT_BIT);
3253 	//	CUIOGLWindow::orthoView();
3254 	//	glColor4ub(0, 0, 0, 255);
3255 	//	glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
3256 	//	glEnable(GL_DEPTH_TEST);
3257 	//	glDisable(GL_LIGHTING);
3258 	//	glDisable(GL_POLYGON_OFFSET_FILL);
3259 	//	glDepthFunc(GL_GREATER);
3260 	//	glDepthRange(0.0f, 1.0f);
3261 	//	glBegin(GL_QUADS);
3262 	//		treGlVertex3f(0.0f, 0.0f, -1.0f);
3263 	//		treGlVertex3f((TCFloat)width, 0.0f, -1.0f);
3264 	//		treGlVertex3f((TCFloat)width, (TCFloat)height, -1.0f);
3265 	//		treGlVertex3f(0.0f, (TCFloat)height, -1.0f);
3266 	//	glEnd();
3267 	//	glPopAttrib();
3268 	//}
3269 }
3270 
updateModelViewerSize(void)3271 void ModelWindow::updateModelViewerSize(void)
3272 {
3273 	double scaleFactor = getScaleFactor(true);
3274 	modelViewer->setWidth(width / scaleFactor);
3275 	modelViewer->setHeight(height / scaleFactor);
3276 	modelViewer->setScaleFactor(scaleFactor);
3277 }
3278 
cleanupRenderSettings(void)3279 void ModelWindow::cleanupRenderSettings(void)
3280 {
3281 	if (!savingFromCommandLine)
3282 	{
3283 		// If we're saving from the command line, there's no need to
3284 		// put things back for regular rendering (particularly
3285 		// recompiling the model, which takes quite a bit of extra
3286 		// time.
3287 		makeCurrent();
3288 		updateModelViewerSize();
3289 		//modelViewer->recompile();
3290 		modelViewer->unpause();
3291 		modelViewer->setup();
3292 	}
3293 }
3294 
cleanupOffscreen(void)3295 void ModelWindow::cleanupOffscreen(void)
3296 {
3297 	hCurrentDC = hdc;
3298 	hCurrentGLRC = hglrc;
3299 	if (hPBuffer)
3300 	{
3301 		cleanupPBuffer();
3302 	}
3303 	else if (hBitmapRenderGLRC)
3304 	{
3305 		cleanupBitmapRender();
3306 	}
3307 	cleanupRenderSettings();
3308 }
3309 
cleanupBitmapRender(void)3310 void ModelWindow::cleanupBitmapRender(void)
3311 {
3312 	if (hBitmapRenderGLRC)
3313 	{
3314 		openGlWillEnd();
3315 		wglDeleteContext(hBitmapRenderGLRC);
3316 		hBitmapRenderGLRC = NULL;
3317 	}
3318 	if (hRenderBitmap)
3319 	{
3320 		DeleteObject(hRenderBitmap);
3321 		hRenderBitmap = NULL;
3322 	}
3323 	if (hBitmapRenderDC)
3324 	{
3325 		DeleteDC(hBitmapRenderDC);
3326 		hBitmapRenderDC = NULL;
3327 	}
3328 	TREGLExtensions::disableAll(false);
3329 	modelViewer->setNeedsRecompile();
3330 }
3331 
cleanupPBuffer(void)3332 void ModelWindow::cleanupPBuffer(void)
3333 {
3334 	if (hPBuffer)
3335 	{
3336 		using namespace TREGLExtensionsNS;
3337 
3338 		if (hPBufferDC)
3339 		{
3340 			if (hPBufferGLRC)
3341 			{
3342 				wglDeleteContext(hPBufferGLRC);
3343 				hPBufferGLRC = NULL;
3344 			}
3345 			wglReleasePbufferDCARB(hPBuffer, hPBufferDC);
3346 			hPBufferDC = NULL;
3347 		}
3348 		wglDestroyPbufferARB(hPBuffer);
3349 		hPBuffer = NULL;
3350 	}
3351 }
3352 
3353 //bool ModelWindow::canSaveAlpha(void)
3354 //{
3355 //	if (saveAlpha && saveImageType == PNG_IMAGE_TYPE_INDEX)
3356 //	{
3357 //		int alphaBits;
3358 //
3359 //		glGetIntegerv(GL_ALPHA_BITS, &alphaBits);
3360 //		return alphaBits > 0;
3361 //	}
3362 //	return false;
3363 //}
3364 
setupSnapshotBackBuffer(int imageWidth,int imageHeight,RECT & rect)3365 void ModelWindow::setupSnapshotBackBuffer(int imageWidth, int imageHeight,
3366 										  RECT &rect)
3367 {
3368 	makeCurrent();
3369 	modelViewer->setSlowClear(true);
3370 	GetWindowRect(hParentWindow, &rect);
3371 	MoveWindow(hParentWindow, 0, 0, rect.right - rect.left,
3372 		rect.bottom - rect.top, TRUE);
3373 	modelViewer->setWidth(imageWidth);
3374 	modelViewer->setHeight(imageHeight);
3375 	modelViewer->setup();
3376 	glReadBuffer(GL_BACK);
3377 }
3378 
cleanupSnapshotBackBuffer(RECT & rect)3379 void ModelWindow::cleanupSnapshotBackBuffer(RECT &rect)
3380 {
3381 	MoveWindow(hParentWindow, rect.left, rect.top, rect.right - rect.left,
3382 		rect.bottom - rect.top, TRUE);
3383 	RedrawWindow(hParentWindow, NULL, NULL,
3384 		RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW);
3385 	updateModelViewerSize();
3386 	modelViewer->setup();
3387 }
3388 
getSaveImageType(void)3389 LDSnapshotTaker::ImageType ModelWindow::getSaveImageType(void)
3390 {
3391 	switch (saveImageType)
3392 	{
3393 	case PNG_IMAGE_TYPE_INDEX:
3394 		return LDSnapshotTaker::ITPng;
3395 	case BMP_IMAGE_TYPE_INDEX:
3396 		return LDSnapshotTaker::ITBmp;
3397 	case JPG_IMAGE_TYPE_INDEX:
3398 		return LDSnapshotTaker::ITJpg;
3399 	case SVG_IMAGE_TYPE_INDEX:
3400 		return LDSnapshotTaker::ITSvg;
3401 	case EPS_IMAGE_TYPE_INDEX:
3402 		return LDSnapshotTaker::ITEps;
3403 	case PDF_IMAGE_TYPE_INDEX:
3404 		return LDSnapshotTaker::ITPdf;
3405 	default:
3406 		return LDSnapshotTaker::ITPng;
3407 	}
3408 }
3409 
grabSetup(int & imageWidth,int & imageHeight,RECT & origRect,bool & origSlowClear)3410 void ModelWindow::grabSetup(
3411 	int &imageWidth,
3412 	int &imageHeight,
3413 	RECT &origRect,
3414 	bool &origSlowClear)
3415 {
3416 	currentAntialiasType = TCUserDefaults::longForKey(FSAA_MODE_KEY);
3417 	int newWidth = 1600;
3418 	int newHeight = 1200;
3419 	int numXTiles, numYTiles;
3420 
3421 	memset(&origRect, 0, sizeof(origRect));
3422 	origSlowClear = modelViewer->getSlowClear();
3423 	offscreenActive = true;
3424 	snapshotTaker->calcTiling(imageWidth, imageHeight, newWidth, newHeight,
3425 		numXTiles, numYTiles);
3426 	if (!setupOffscreen(newWidth, newHeight, currentAntialiasType > 0))
3427 	{
3428 		newWidth = width;		// width is OpenGL window width
3429 		newHeight = height;		// height is OpenGL window height
3430 		snapshotTaker->calcTiling(imageWidth, imageHeight, newWidth, newHeight,
3431 			numXTiles, numYTiles);
3432 		setupSnapshotBackBuffer(newWidth, newHeight, origRect);
3433 	}
3434 }
3435 
grabCleanup(RECT origRect,bool origSlowClear)3436 void ModelWindow::grabCleanup(RECT origRect, bool origSlowClear)
3437 {
3438 	if (snapshotTaker->getUseFBO())
3439 	{
3440 		snapshotTaker->set16BPC(false);
3441 		cleanupRenderSettings();
3442 	}
3443 	else if (hPBuffer || hBitmapRenderGLRC)
3444 	{
3445 		cleanupOffscreen();
3446 	}
3447 	else
3448 	{
3449 		cleanupSnapshotBackBuffer(origRect);
3450 	}
3451 	offscreenActive = false;
3452 	modelViewer->setSlowClear(origSlowClear);
3453 }
3454 
saveImage(UCSTR filename,int imageWidth,int imageHeight,bool zoomToFit)3455 bool ModelWindow::saveImage(
3456 	UCSTR filename,
3457 	int imageWidth,
3458 	int imageHeight,
3459 	bool zoomToFit)
3460 {
3461 	RECT origRect;
3462 	bool origSlowClear;
3463 	bool retValue = false;
3464 
3465 	if (!snapshotTaker)
3466 	{
3467 		snapshotTaker =  new LDSnapshotTaker(modelViewer);
3468 		// Only try FBO if the user hasn't unchecked the "Use Pixel Buffer"
3469 		// check box.  Note that snapshotTaker will also check that the
3470 		// FBO extension is available before using it.
3471 		if (LDVExtensionsSetup::havePixelBufferExtension())
3472 		{
3473 			snapshotTaker->setUseFBO(true);
3474 		}
3475 	}
3476 	snapshotTaker->setImageType(getSaveImageType());
3477 	snapshotTaker->setTrySaveAlpha(saveAlpha);
3478 	snapshotTaker->setAutoCrop(autoCrop);
3479 	std::string productVersion;
3480 	ucstringtoutf8(productVersion,
3481 		LDViewWindow::getProductVersion());
3482 	snapshotTaker->setProductVersion(productVersion);
3483 	grabSetup(imageWidth, imageHeight, origRect, origSlowClear);
3484 	std::string utf8Filename;
3485 	ucstringtoutf8(utf8Filename, filename);
3486 	retValue = snapshotTaker->saveImage(utf8Filename.c_str(), imageWidth,
3487 		imageHeight, zoomToFit);
3488 	grabCleanup(origRect, origSlowClear);
3489 	return retValue;
3490 }
3491 
doDialogInit(HWND hDlg,HWND,LPARAM)3492 BOOL ModelWindow::doDialogInit(HWND hDlg, HWND /*hFocusWindow*/,
3493 							   LPARAM /*lParam*/)
3494 {
3495 	if (hDlg == hPrintDialog)
3496 	{
3497 		setupPrintExtras();
3498 	}
3499 	else if (hDlg == hSaveDialog)
3500 	{
3501 		setupSaveExtras();
3502 	}
3503 	else if (hDlg == hPageSetupDialog)
3504 	{
3505 		setupPageSetupExtras();
3506 	}
3507 	return TRUE;
3508 }
3509 
staticPrintHook(HWND hDlg,UINT uiMsg,WPARAM wParam,LPARAM lParam)3510 UINT_PTR CALLBACK ModelWindow::staticPrintHook(
3511 	HWND hDlg,
3512 	UINT uiMsg,
3513 	WPARAM wParam,
3514 	LPARAM lParam)
3515 {
3516 	ModelWindow* modelWindow = (ModelWindow*)GetWindowLongPtr(hDlg,
3517 		GWLP_USERDATA);
3518 
3519 	if (uiMsg == WM_INITDIALOG)
3520 	{
3521 		modelWindow = (ModelWindow*)((PRINTDLG*)lParam)->lCustData;
3522 		if (modelWindow)
3523 		{
3524 			modelWindow->hPrintDialog = hDlg;
3525 			SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)modelWindow);
3526 		}
3527 	}
3528 	if (modelWindow)
3529 	{
3530 		return modelWindow->dialogProc(hDlg, uiMsg, wParam, lParam);
3531 	}
3532 	else
3533 	{
3534 		return 0;
3535 	}
3536 }
3537 
selectPrinter(PRINTDLG & pd)3538 bool ModelWindow::selectPrinter(PRINTDLG &pd)
3539 {
3540 	HGLOBAL hDevMode = GlobalAlloc(GHND, sizeof(DEVMODE));
3541 	DEVMODE *devMode = (DEVMODE *)GlobalLock(hDevMode);
3542 	bool retValue;
3543 
3544 	devMode->dmFields = DM_ORIENTATION | DM_PAPERSIZE;
3545 	devMode->dmOrientation = (short)printOrientation;
3546 	devMode->dmPaperSize = (short)printPaperSize;
3547 	GlobalUnlock(hDevMode);
3548 	// Initialize the PRINTDLG members.
3549  	pd.lStructSize = sizeof(PRINTDLG);
3550 	pd.hDevMode = hDevMode;
3551 	pd.hDevNames = NULL;
3552 	pd.Flags = PD_RETURNDC | PD_NOPAGENUMS | PD_NOSELECTION;// |
3553 //		PD_ENABLEPRINTHOOK | PD_ENABLEPRINTTEMPLATE;
3554 	pd.hwndOwner = NULL;//parentWindow->getHWindow();
3555 	pd.hDC = NULL;
3556 	pd.nFromPage = 1;
3557 	pd.nToPage = 1;
3558 	pd.nMinPage = 0;
3559 	pd.nMaxPage = 0;
3560 	pd.nCopies = 1;
3561 	pd.hInstance = getLanguageModule();
3562 	pd.lCustData = (LPARAM)this;
3563 	pd.lpfnPrintHook = staticPrintHook;
3564 	pd.lpfnSetupHook = NULL;
3565 	pd.lpPrintTemplateName = MAKEINTRESOURCE(PRINTDLGORD);
3566 	pd.lpSetupTemplateName = NULL;
3567 	pd.hPrintTemplate = NULL;
3568 	pd.hSetupTemplate = NULL;
3569 
3570 	// Display the PRINT dialog box.
3571 
3572 	if (PrintDlg(&pd))
3573 	{
3574 		hPrintDialog = NULL;
3575 		retValue = true;
3576 	}
3577 	else
3578 	{
3579 //		DWORD error = CommDlgExtendedError();
3580 		hPrintDialog = NULL;
3581 		retValue = false;
3582 	}
3583 	GlobalFree(hDevMode);
3584 	return retValue;
3585 }
3586 
swap(int & left,int & right)3587 void ModelWindow::swap(int &left, int &right)
3588 {
3589 	int temp = left;
3590 
3591 	left = right;
3592 	right = temp;
3593 }
3594 
calcTiling(int desiredWidth,int desiredHeight,int & bitmapWidth,int & bitmapHeight,int & numXTiles,int & numYTiles)3595 void ModelWindow::calcTiling(int desiredWidth, int desiredHeight,
3596 							 int &bitmapWidth, int &bitmapHeight,
3597 							 int &numXTiles, int &numYTiles)
3598 {
3599 	if (desiredWidth > bitmapWidth)
3600 	{
3601 		numXTiles = (desiredWidth + bitmapWidth - 1) / bitmapWidth;
3602 	}
3603 	else
3604 	{
3605 		numXTiles = 1;
3606 	}
3607 	bitmapWidth = desiredWidth / numXTiles;
3608 	if (desiredHeight > bitmapHeight)
3609 	{
3610 		numYTiles = (desiredHeight + bitmapHeight - 1) / bitmapHeight;
3611 	}
3612 	else
3613 	{
3614 		numYTiles = 1;
3615 	}
3616 	bitmapHeight = desiredHeight / numYTiles;
3617 }
3618 
calcTiling(HDC hPrinterDC,int & bitmapWidth,int & bitmapHeight,int & numXTiles,int & numYTiles)3619 void ModelWindow::calcTiling(HDC hPrinterDC, int &bitmapWidth,
3620 							 int &bitmapHeight, int &numXTiles, int &numYTiles)
3621 {
3622 	int hRes;
3623 	int vRes;
3624 	int printXOffset;
3625 	int printYOffset;
3626 	int printWidth;
3627 	int printHeight;
3628 	int printRMarginPixels;
3629 	int printBMarginPixels;
3630 	int maxBitmapWidth = bitmapWidth;
3631 	int maxBitmapHeight = bitmapHeight;
3632 
3633 	printHDPI = GetDeviceCaps(hPrinterDC, LOGPIXELSX);
3634 	printVDPI = GetDeviceCaps(hPrinterDC, LOGPIXELSY);
3635 	printXOffset = GetDeviceCaps(hPrinterDC, PHYSICALOFFSETX);
3636 	printYOffset = GetDeviceCaps(hPrinterDC, PHYSICALOFFSETY);
3637 	printWidth = GetDeviceCaps(hPrinterDC, PHYSICALWIDTH);
3638 	printHeight = GetDeviceCaps(hPrinterDC, PHYSICALHEIGHT);
3639 	hRes = GetDeviceCaps(hPrinterDC, HORZRES);
3640 	vRes = GetDeviceCaps(hPrinterDC, VERTRES);
3641 	printLMarginPixels = (int)(printLeftMargin * printHDPI) - printXOffset;
3642 	printRMarginPixels = (int)(printRightMargin * printHDPI) + hRes -
3643 		printWidth + printXOffset;
3644 	printTMarginPixels = (int)(printTopMargin * printVDPI) - printYOffset;
3645 	printBMarginPixels = (int)(printBottomMargin * printVDPI) + vRes -
3646 		printHeight + printYOffset;
3647 	bitmapWidth = hRes - printLMarginPixels - printRMarginPixels;
3648 	if (bitmapWidth > maxBitmapWidth)
3649 	{
3650 		numXTiles = (bitmapWidth + maxBitmapWidth - 1) / maxBitmapWidth;
3651 	}
3652 	else
3653 	{
3654 		numXTiles = 1;
3655 	}
3656 	bitmapWidth = bitmapWidth / numXTiles;
3657 	bitmapHeight = vRes - printTMarginPixels - printBMarginPixels;
3658 	if (bitmapHeight > maxBitmapHeight)
3659 	{
3660 		numYTiles = (bitmapHeight + maxBitmapHeight - 1) / maxBitmapHeight;
3661 	}
3662 	else
3663 	{
3664 		numYTiles = 1;
3665 	}
3666 	bitmapHeight = bitmapHeight / numYTiles;
3667 }
3668 
printPage(const PRINTDLG & pd)3669 bool ModelWindow::printPage(const PRINTDLG &pd)
3670 {
3671 	bool retValue = false;
3672 	HDC hBitmapDC = CreateCompatibleDC(pd.hDC);
3673 
3674 	if (hBitmapDC)
3675 	{
3676 		int bitmapWidth = 1600;
3677 		int bitmapHeight = 1200;
3678 		int renderWidth;
3679 		int renderHeight;
3680 		int numXTiles;
3681 		int numYTiles;
3682 		bool oldSlowClear = modelViewer->getSlowClear();
3683 		int oldR = modelViewer->getBackgroundR();
3684 		int oldG = modelViewer->getBackgroundG();
3685 		int oldB = modelViewer->getBackgroundB();
3686 		int oldA = modelViewer->getBackgroundA();
3687 		LDVStereoMode oldStereoMode = modelViewer->getStereoMode();
3688 		BYTE *bmBuffer = NULL;
3689 		HBITMAP hBitmap = NULL;
3690 		bool canceled = false;
3691 		bool landscape = printOrientation == DMORIENT_LANDSCAPE;
3692 
3693 		calcTiling(pd.hDC, bitmapWidth, bitmapHeight, numXTiles, numYTiles);
3694 		if (landscape)
3695 		{
3696 			renderWidth = bitmapHeight;
3697 			renderHeight = bitmapWidth;
3698 			swap(numXTiles, numYTiles);
3699 		}
3700 		else
3701 		{
3702 			renderWidth = bitmapWidth;
3703 			renderHeight = bitmapHeight;
3704 		}
3705 		offscreenActive = true;
3706 		if (printHDPI != printVDPI)
3707 		{
3708 			modelViewer->setPixelAspectRatio((float)printHDPI / printVDPI);
3709 		}
3710 		if (printBackground)
3711 		{
3712 			modelViewer->setBackgroundRGBA(oldR, oldG, oldB, oldA);
3713 		}
3714 		else
3715 		{
3716 			modelViewer->setBackgroundRGBA(255, 255, 255, 255);
3717 		}
3718 		modelViewer->setStereoMode(LDVStereoNone);
3719 		if (!setupOffscreen(renderWidth, renderHeight))
3720 		{
3721 			if (!modelViewer->getCompiled())
3722 			{
3723 				canceled = true;
3724 			}
3725 			else
3726 			{
3727 				if (landscape)
3728 				{
3729 					bitmapWidth = height;
3730 					bitmapHeight = width;
3731 					swap(numXTiles, numYTiles);
3732 				}
3733 				else
3734 				{
3735 					bitmapWidth = width;
3736 					bitmapHeight = height;
3737 				}
3738 				calcTiling(pd.hDC, bitmapWidth, bitmapHeight, numXTiles,
3739 					numYTiles);
3740 				if (landscape)
3741 				{
3742 					renderWidth = bitmapHeight;
3743 					renderHeight = bitmapWidth;
3744 				}
3745 				else
3746 				{
3747 					renderWidth = bitmapWidth;
3748 					renderHeight = bitmapHeight;
3749 				}
3750 				modelViewer->setWidth(renderWidth);
3751 				modelViewer->setHeight(renderHeight);
3752 				makeCurrent();
3753 				glReadBuffer(GL_BACK);
3754 				modelViewer->setSlowClear(true);
3755 			}
3756 		}
3757 		if (!canceled)
3758 		{
3759 			hBitmap = createDIBSection(hBitmapDC, bitmapWidth, bitmapHeight,
3760 				&bmBuffer);
3761 		}
3762 		if (hBitmap)
3763 		{
3764 			int xTile, yTile;
3765 			int renderLineSize = roundUp(renderWidth * 3, 4);
3766 			BYTE *buffer = new BYTE[renderLineSize * renderHeight];
3767 			TCFloat32 oldHighlightLineWidth =
3768 				modelViewer->getHighlightLineWidth();
3769 			TCFloat32 oldWireframeLineWidth =
3770 				modelViewer->getWireframeLineWidth();
3771 
3772 			modelViewer->setNumXTiles(numXTiles);
3773 			modelViewer->setNumYTiles(numYTiles);
3774 			if (landscape)
3775 			{
3776 				swap(numXTiles, numYTiles);
3777 			}
3778 			modelViewer->setHighlightLineWidth(printHDPI / 100 *
3779 				oldHighlightLineWidth);
3780 			modelViewer->setWireframeLineWidth(printHDPI / 100 *
3781 				oldWireframeLineWidth);
3782 			for (yTile = 0; yTile < numYTiles && !canceled; yTile++)
3783 			{
3784 				if (landscape)
3785 				{
3786 					modelViewer->setXTile(yTile);
3787 				}
3788 				else
3789 				{
3790 					modelViewer->setYTile(yTile);
3791 				}
3792 				progressCallback(ls(_UC("PrintingModel")),
3793 					0.0f, false);
3794 				for (xTile = 0; xTile < numXTiles && !canceled; xTile++)
3795 				{
3796 					int lx, ly;
3797 
3798 					if (progressCallback((CUCSTR)NULL, (float)(yTile *
3799 						numXTiles + xTile) / (numYTiles * numXTiles), false))
3800 					{
3801 						if (landscape)
3802 						{
3803 							modelViewer->setYTile(numXTiles - xTile - 1);
3804 						}
3805 						else
3806 						{
3807 							modelViewer->setXTile(xTile);
3808 						}
3809 						renderOffscreenImage();
3810 						glReadPixels(0, 0, renderWidth, renderHeight, GL_RGB,
3811 							GL_UNSIGNED_BYTE, buffer);
3812 						if (landscape)
3813 						{
3814 							int bitmapLineSize = roundUp(bitmapWidth * 3, 4);
3815 
3816 							for (ly = 0; ly < renderHeight; ly++)
3817 							{
3818 								int offset = ly * renderLineSize;
3819 								for (lx = 0; lx < renderWidth; lx++)
3820 								{
3821 									int renderSpot = offset + lx * 3;
3822 									int bitmapSpot = (renderWidth - lx - 1)
3823 										* bitmapLineSize + ly * 3;
3824 
3825 									bmBuffer[bitmapSpot] =
3826 										buffer[renderSpot + 2];
3827 									bmBuffer[bitmapSpot + 1] =
3828 										buffer[renderSpot + 1];
3829 									bmBuffer[bitmapSpot + 2] =
3830 										buffer[renderSpot];
3831 								}
3832 							}
3833 						}
3834 						else
3835 						{
3836 							for (ly = 0; ly < bitmapHeight; ly++)
3837 							{
3838 								int offset = ly * renderLineSize;
3839 
3840 								for (lx = 0; lx < bitmapWidth; lx++)
3841 								{
3842 									int spot = offset + lx * 3;
3843 
3844 									bmBuffer[spot] = buffer[spot + 2];
3845 									bmBuffer[spot + 1] = buffer[spot + 1];
3846 									bmBuffer[spot + 2] = buffer[spot];
3847 								}
3848 							}
3849 						}
3850 						SelectObject(hBitmapDC, hBitmap);
3851 						if (BitBlt(pd.hDC,
3852 							printLMarginPixels + xTile * bitmapWidth,
3853 							printTMarginPixels + yTile * bitmapHeight,
3854 							bitmapWidth, bitmapHeight, hBitmapDC, 0, 0,
3855 							SRCCOPY))
3856 						{
3857 							retValue = true;
3858 						}
3859 					}
3860 					else
3861 					{
3862 						canceled = true;
3863 						retValue = false;
3864 					}
3865 				}
3866 			}
3867 			progressCallback((CUCSTR)NULL, 1.0f, false);
3868 			DeleteObject(hBitmap);
3869 			modelViewer->setHighlightLineWidth(oldHighlightLineWidth);
3870 			modelViewer->setWireframeLineWidth(oldWireframeLineWidth);
3871 			modelViewer->setXTile(0);
3872 			modelViewer->setYTile(0);
3873 			modelViewer->setNumXTiles(1);
3874 			modelViewer->setNumYTiles(1);
3875 			delete buffer;
3876 			progressCallback((CUCSTR)NULL, 2.0f, false);
3877 		}
3878 		if (hPBuffer)
3879 		{
3880 			cleanupOffscreen();
3881 		}
3882 		else
3883 		{
3884 			updateModelViewerSize();
3885 			modelViewer->setSlowClear(oldSlowClear);
3886 		}
3887 		modelViewer->setBackgroundRGBA(oldR, oldG, oldB, oldA);
3888 		modelViewer->setStereoMode(oldStereoMode);
3889 		modelViewer->setPixelAspectRatio(1.0f);
3890 		DeleteDC(hBitmapDC);
3891 	}
3892 	offscreenActive = false;
3893 	return retValue;
3894 }
3895 
parseDevMode(HGLOBAL hDevMode)3896 bool ModelWindow::parseDevMode(HGLOBAL hDevMode)
3897 {
3898 	DEVMODE *devMode = (DEVMODE *)GlobalLock(hDevMode);
3899 	bool retValue = true;
3900 
3901 	if (devMode)
3902 	{
3903 		if (devMode->dmOrientation != printOrientation)
3904 		{
3905 			printOrientation = devMode->dmOrientation;
3906 			TCUserDefaults::setLongForKey(printOrientation, ORIENTATION_KEY, false);
3907 		}
3908 		if (devMode->dmPaperSize != printPaperSize)
3909 		{
3910 			if (devMode->dmPaperSize < DMPAPER_USER)
3911 			{
3912 				printPaperSize = devMode->dmPaperSize;
3913 				TCUserDefaults::setLongForKey(printPaperSize, PAPER_SIZE_KEY,
3914 					false);
3915 			}
3916 			else
3917 			{
3918 				retValue = false;
3919 			}
3920 		}
3921 		GlobalUnlock(hDevMode);
3922 	}
3923 	return retValue;
3924 }
3925 
staticPageSetupHook(HWND hDlg,UINT uiMsg,WPARAM wParam,LPARAM lParam)3926 UINT_PTR CALLBACK ModelWindow::staticPageSetupHook(
3927 	HWND hDlg,
3928 	UINT uiMsg,
3929 	WPARAM wParam,
3930 	LPARAM lParam)
3931 {
3932 	ModelWindow* modelWindow = (ModelWindow*)GetWindowLongPtr(hDlg,
3933 		GWLP_USERDATA);
3934 
3935 	if (uiMsg == WM_INITDIALOG)
3936 	{
3937 		modelWindow = (ModelWindow*)((PAGESETUPDLG*)lParam)->lCustData;
3938 		if (modelWindow)
3939 		{
3940 			modelWindow->hPageSetupDialog = hDlg;
3941 			SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)modelWindow);
3942 		}
3943 	}
3944 	if (modelWindow)
3945 	{
3946 		return modelWindow->dialogProc(hDlg, uiMsg, wParam, lParam);
3947 	}
3948 	else
3949 	{
3950 		return 0;
3951 	}
3952 }
3953 
pageSetup(void)3954 bool ModelWindow::pageSetup(void)
3955 {
3956 	PAGESETUPDLG psd;
3957 	HGLOBAL hDevMode = GlobalAlloc(GHND, sizeof(DEVMODE));
3958 	DEVMODE *devMode = (DEVMODE *)GlobalLock(hDevMode);
3959 	bool retValue;
3960 
3961 	devMode->dmFields = DM_ORIENTATION | DM_PAPERSIZE;
3962 	devMode->dmOrientation = (short)printOrientation;
3963 	devMode->dmPaperSize = (short)printPaperSize;
3964 	GlobalUnlock(hDevMode);
3965 	psd.lStructSize = sizeof(PAGESETUPDLG);
3966 	psd.hwndOwner = GetParent(hWindow);
3967 	psd.hDevMode = hDevMode;
3968 	psd.hDevNames = NULL;
3969 	psd.Flags = PSD_DISABLEPRINTER | PSD_INTHOUSANDTHSOFINCHES | PSD_MARGINS |
3970 		PSD_ENABLEPAGESETUPTEMPLATE | PSD_ENABLEPAGESETUPHOOK;
3971 	psd.rtMargin.left = (long)(printLeftMargin * 1000);
3972 	psd.rtMargin.right = (long)(printRightMargin * 1000);
3973 	psd.rtMargin.top = (long)(printTopMargin * 1000);
3974 	psd.rtMargin.bottom = (long)(printBottomMargin * 1000);
3975 	psd.lCustData = (LPARAM)this;
3976 	psd.hInstance = getLanguageModule();
3977 	psd.lpfnPageSetupHook = staticPageSetupHook;
3978 	psd.lpPageSetupTemplateName = MAKEINTRESOURCE(PAGESETUPDLGORD);
3979 
3980 	stopAnimation();
3981 	if (PageSetupDlg(&psd))
3982 	{
3983 		printLeftMargin = psd.rtMargin.left / 1000.0f;
3984 		printRightMargin = psd.rtMargin.right / 1000.0f;
3985 		printTopMargin = psd.rtMargin.top / 1000.0f;
3986 		printBottomMargin = psd.rtMargin.bottom / 1000.0f;
3987 		if (!parseDevMode(psd.hDevMode))
3988 		{
3989 			MessageBox(hParentWindow,
3990 				ls(_UC("PrintCustomPaperError")),
3991 				ls(_UC("PrintPaperSize")),
3992 				MB_OK | MB_ICONEXCLAMATION);
3993 		}
3994 		TCUserDefaults::setLongForKey(psd.rtMargin.left, LEFT_MARGIN_KEY,
3995 			false);
3996 		TCUserDefaults::setLongForKey(psd.rtMargin.right, RIGHT_MARGIN_KEY,
3997 			false);
3998 		TCUserDefaults::setLongForKey(psd.rtMargin.top, TOP_MARGIN_KEY, false);
3999 		TCUserDefaults::setLongForKey(psd.rtMargin.bottom, BOTTOM_MARGIN_KEY,
4000 			false);
4001 		TCUserDefaults::setLongForKey(printBackground ? 1 : 0,
4002 			PRINT_BACKGROUND_KEY, false);
4003 		retValue = true;
4004 	}
4005 	else
4006 	{
4007 		retValue = false;
4008 	}
4009 	GlobalFree(hDevMode);
4010 	return retValue;
4011 }
4012 
print(void)4013 bool ModelWindow::print(void)
4014 {
4015 	PRINTDLG pd;
4016 	bool retValue = false;
4017 
4018 	stopAnimation();
4019 	memset(&pd, 0, sizeof(pd));
4020 	pd.lStructSize = sizeof(pd);
4021 	if (selectPrinter(pd))
4022 	{
4023 		DOCINFO di;
4024 		int printJobId;
4025 		CUCSTR docName = parentWindow->getWindowTitle();
4026 
4027 		parseDevMode(pd.hDevMode);
4028 		TCUserDefaults::setLongForKey(usePrinterDPI ? 1 : 0,
4029 			USE_PRINTER_DPI_KEY);
4030 		TCUserDefaults::setLongForKey(printDPI, PRINT_DPI_KEY);
4031 		TCUserDefaults::setLongForKey(printBackground ? 1 : 0,
4032 			PRINT_BACKGROUND_KEY, false);
4033 
4034 		di.cbSize = sizeof(DOCINFO);
4035 		di.lpszDocName = docName;
4036 		di.lpszOutput = NULL;
4037 		di.lpszDatatype = NULL;
4038 		di.fwType = 0;
4039 		printJobId = StartDoc(pd.hDC, &di);
4040 		if (printJobId > 0 && StartPage(pd.hDC) > 0)
4041 		{
4042 			if (printPage(pd))
4043 			{
4044 				retValue = true;
4045 				EndPage(pd.hDC);
4046 				EndDoc(pd.hDC);
4047 			}
4048 			else
4049 			{
4050 				EndPage(pd.hDC);
4051 				AbortDoc(pd.hDC);
4052 			}
4053 		}
4054 	}
4055 	return retValue;
4056 }
4057 
disableSaveSize(void)4058 void ModelWindow::disableSaveSize(void)
4059 {
4060 	enableSaveSize(FALSE);
4061 }
4062 
enableSaveSize(BOOL enable)4063 void ModelWindow::enableSaveSize(BOOL enable /*= TRUE*/)
4064 {
4065 	EnableWindow(hSaveWidthLabel, enable);
4066 	EnableWindow(hSaveWidth, enable);
4067 	EnableWindow(hSaveHeightLabel, enable);
4068 	EnableWindow(hSaveHeight, enable);
4069 	EnableWindow(hSaveZoomToFitButton, enable);
4070 	if (enable)
4071 	{
4072 		CUIDialog::windowSetValue(hSaveDialog, IDC_SAVE_WIDTH, saveWidth);
4073 		SendDlgItemMessage(hSaveDialog, IDC_SAVE_WIDTH, EM_LIMITTEXT, 4, 0);
4074 		CUIDialog::windowSetValue(hSaveDialog, IDC_SAVE_HEIGHT, saveHeight);
4075 		SendDlgItemMessage(hSaveDialog, IDC_SAVE_HEIGHT, EM_LIMITTEXT, 4, 0);
4076 		CUIDialog::buttonSetChecked(hSaveDialog, IDC_SAVE_ZOOMTOFIT,
4077 			saveZoomToFit);
4078 	}
4079 	else
4080 	{
4081 		CUIDialog::windowSetText(hSaveDialog, IDC_SAVE_WIDTH, _UC(""));
4082 		CUIDialog::windowSetText(hSaveDialog, IDC_SAVE_HEIGHT, _UC(""));
4083 		CUIDialog::buttonSetChecked(hSaveDialog, IDC_SAVE_ZOOMTOFIT, false);
4084 	}
4085 }
4086 
disableSaveSeries(void)4087 void ModelWindow::disableSaveSeries(void)
4088 {
4089 	enableSaveSeries(FALSE);
4090 }
4091 
enableSaveSeries(BOOL enable)4092 void ModelWindow::enableSaveSeries(BOOL enable /*= TRUE*/)
4093 {
4094 	EnableWindow(hSaveDigitsLabel, enable);
4095 	EnableWindow(hSaveDigitsField, enable);
4096 	EnableWindow(hSaveDigitsSpin, enable);
4097 	if (enable)
4098 	{
4099 		SendDlgItemMessage(hSaveDialog, IDC_SAVE_DIGITS_SPIN, UDM_SETPOS, 0,
4100 			MAKELONG(saveDigits, 0));
4101 	}
4102 	else
4103 	{
4104 		CUIDialog::windowSetText(hSaveDialog, IDC_SAVE_DIGITS, _UC(""));
4105 	}
4106 }
4107 
disableSaveAllSteps(void)4108 void ModelWindow::disableSaveAllSteps(void)
4109 {
4110 	enableSaveAllSteps(FALSE);
4111 }
4112 
enableSaveAllSteps(BOOL enable)4113 void ModelWindow::enableSaveAllSteps(BOOL enable /*= TRUE*/)
4114 {
4115 	BOOL sameScaleEnable = enable && !saveActualSize && saveZoomToFit;
4116 
4117 	EnableWindow(hSaveStepSuffixLabel, enable);
4118 	EnableWindow(hSaveStepSuffixField, enable);
4119 	EnableWindow(hSaveStepsSameScaleButton, sameScaleEnable);
4120 	if (enable)
4121 	{
4122 		CUIDialog::windowSetText(hSaveDialog, IDC_STEP_SUFFIX, saveStepSuffix);
4123 	}
4124 	else
4125 	{
4126 		CUIDialog::windowSetText(hSaveDialog, IDC_STEP_SUFFIX, _UC(""));
4127 	}
4128 	if (sameScaleEnable)
4129 	{
4130 		CUIDialog::buttonSetChecked(hSaveDialog, IDC_SAME_SCALE,
4131 			saveStepsSameScale);
4132 	}
4133 	else
4134 	{
4135 		CUIDialog::buttonSetChecked(hSaveDialog, IDC_SAME_SCALE, false);
4136 	}
4137 }
4138 
updatePrintDPIField(void)4139 void ModelWindow::updatePrintDPIField(void)
4140 {
4141 	if (usePrinterDPI)
4142 	{
4143 		EnableWindow(hPrintDPI, FALSE);
4144 		CUIDialog::windowSetText(hPrintDialog, IDC_PRINT_DPI, _UC(""));
4145 	}
4146 	else
4147 	{
4148 		EnableWindow(hPrintDPI, TRUE);
4149 		CUIDialog::windowSetValue(hPrintDialog, IDC_PRINT_DPI, printDPI);
4150 	}
4151 }
4152 
setupPrintExtras(void)4153 void ModelWindow::setupPrintExtras(void)
4154 {
4155 	HWND hIconWindow = GetDlgItem(hPrintDialog, 1086);
4156 	RECT iconRect;
4157 	POINT iconPoint;
4158 
4159 	hPrintDPI = GetDlgItem(hPrintDialog, IDC_PRINT_DPI);
4160 	GetWindowRect(hIconWindow, &iconRect);
4161 	iconPoint.x = iconRect.left;
4162 	iconPoint.y = iconRect.top;
4163 	ScreenToClient(hPrintDialog, &iconPoint);
4164 	MoveWindow(hIconWindow, iconPoint.x, iconPoint.y, 114, 36, TRUE);
4165 	CUIDialog::buttonSetChecked(hPrintDialog, IDC_PRINT_PRINTER_DPI,
4166 		usePrinterDPI);
4167 	CUIDialog::buttonSetChecked(hPrintDialog, IDC_PRINT_SPECIFY_DPI,
4168 		!usePrinterDPI);
4169 	CUIDialog::buttonSetChecked(hPrintDialog, IDC_PRINT_BACKGROUND,
4170 		printBackground);
4171 	SendDlgItemMessage(hPrintDialog, IDC_PRINT_DPI, EM_LIMITTEXT, 4, 0);
4172 	updatePrintDPIField();
4173 }
4174 
setupPageSetupExtras(void)4175 void ModelWindow::setupPageSetupExtras(void)
4176 {
4177 	CUIDialog::buttonSetChecked(hPageSetupDialog, IDC_PRINT_BACKGROUND,
4178 		printBackground);
4179 }
4180 
updateSaveSizeEnabled(void)4181 void ModelWindow::updateSaveSizeEnabled(void)
4182 {
4183 	if (saveActualSize)
4184 	{
4185 		disableSaveSize();
4186 	}
4187 	else
4188 	{
4189 		enableSaveSize();
4190 	}
4191 }
4192 
updateSaveSeriesEnabled(void)4193 void ModelWindow::updateSaveSeriesEnabled(void)
4194 {
4195 	if (saveSeries)
4196 	{
4197 		enableSaveSeries();
4198 	}
4199 	else
4200 	{
4201 		disableSaveSeries();
4202 	}
4203 }
4204 
updateSaveAllStepsEnabled(void)4205 void ModelWindow::updateSaveAllStepsEnabled(void)
4206 {
4207 	if (saveAllSteps)
4208 	{
4209 		enableSaveAllSteps();
4210 	}
4211 	else
4212 	{
4213 		disableSaveAllSteps();
4214 	}
4215 }
4216 
saveTypeUpdated(void)4217 void ModelWindow::saveTypeUpdated(void)
4218 {
4219 	switch (curSaveOp)
4220 	{
4221 	case LDPreferences::SOExport:
4222 		saveExportTypeUpdated();
4223 		break;
4224 	case LDPreferences::SOSnapshot:
4225 	default:
4226 		saveImageTypeUpdated();
4227 		break;
4228 	}
4229 }
4230 
saveImageTypeUpdated(void)4231 void ModelWindow::saveImageTypeUpdated(void)
4232 {
4233 	BOOL enable = FALSE;
4234 	WPARAM transBg = 0;
4235 
4236 	EnableWindow(hSaveOptionsButton, saveImageType == JPG_IMAGE_TYPE_INDEX);
4237 	if (saveImageType == PNG_IMAGE_TYPE_INDEX)
4238 	{
4239 		enable = TRUE;
4240 		transBg = saveAlpha ? 1 : 0;
4241 	}
4242 	buttonSetChecked(hSaveTransBgButton, transBg);
4243 	EnableWindow(hSaveTransBgButton, enable);
4244 }
4245 
saveExportTypeUpdated(void)4246 void ModelWindow::saveExportTypeUpdated(void)
4247 {
4248 	BOOL enable = FALSE;
4249 	const LDExporter *exporter;
4250 
4251 	exporter = modelViewer->getExporter(
4252 		(LDrawModelViewer::ExportType)saveExportType);
4253 	if (exporter != NULL && exporter->getSettings().size() > 0)
4254 	{
4255 		enable = TRUE;
4256 	}
4257 	EnableWindow(hSaveOptionsButton, enable);
4258 }
4259 
setupSaveExtras(void)4260 void ModelWindow::setupSaveExtras(void)
4261 {
4262 	hSaveOptionsButton = GetDlgItem(hSaveDialog, IDC_SAVE_OPTIONS);
4263 	hSaveTransBgButton = GetDlgItem(hSaveDialog, IDC_TRANSPARENT_BACKGROUND);
4264 	hSaveWidthLabel = GetDlgItem(hSaveDialog, IDC_SAVE_WIDTH_LABEL);
4265 	hSaveWidth = GetDlgItem(hSaveDialog, IDC_SAVE_WIDTH);
4266 	hSaveHeightLabel = GetDlgItem(hSaveDialog, IDC_SAVE_HEIGHT_LABEL);
4267 	hSaveHeight = GetDlgItem(hSaveDialog, IDC_SAVE_HEIGHT);
4268 	hSaveZoomToFitButton = GetDlgItem(hSaveDialog, IDC_SAVE_ZOOMTOFIT);
4269 	hSaveDigitsLabel = GetDlgItem(hSaveDialog, IDC_SAVE_DIGITS_LABEL);
4270 	hSaveDigitsField = GetDlgItem(hSaveDialog, IDC_SAVE_DIGITS);
4271 	hSaveDigitsSpin = GetDlgItem(hSaveDialog, IDC_SAVE_DIGITS_SPIN);
4272 	hSaveStepSuffixLabel = GetDlgItem(hSaveDialog, IDC_STEP_SUFFIX_LABEL);
4273 	hSaveStepSuffixField = GetDlgItem(hSaveDialog, IDC_STEP_SUFFIX);
4274 	hSaveStepsSameScaleButton = GetDlgItem(hSaveDialog, IDC_SAME_SCALE);
4275 	CUIDialog::buttonSetChecked(hSaveDialog, IDC_SAVE_ACTUAL_SIZE,
4276 		!saveActualSize);
4277 	CUIDialog::buttonSetChecked(hSaveDialog, IDC_SAVE_SERIES, saveSeries);
4278 	SendDlgItemMessage(hSaveDialog, IDC_SAVE_DIGITS_SPIN, UDM_SETRANGE32, 1, 5);
4279 	SendDlgItemMessage(hSaveDialog, IDC_SAVE_DIGITS_SPIN, UDM_SETPOS, 0,
4280 		MAKELONG(saveDigits, 0));
4281 	CUIDialog::buttonSetChecked(hSaveDialog, IDC_IGNORE_PBUFFER, ignorePBuffer);
4282 	CUIDialog::buttonSetChecked(hSaveDialog, IDC_IGNORE_FBO, ignoreFBO);
4283 	CUIDialog::buttonSetChecked(hSaveDialog, IDC_IGNORE_PIXEL_FORMAT,
4284 		ignorePixelFormat);
4285 	CUIDialog::buttonSetChecked(hSaveDialog, IDC_AUTO_CROP, autoCrop);
4286 	CUIDialog::buttonSetChecked(hSaveDialog, IDC_ALL_STEPS, saveAllSteps);
4287 	updateSaveSizeEnabled();
4288 	updateSaveSeriesEnabled();
4289 	updateSaveAllStepsEnabled();
4290 	saveTypeUpdated();
4291 }
4292 
updateSaveWidth(void)4293 void ModelWindow::updateSaveWidth(void)
4294 {
4295 	if (!saveActualSize)
4296 	{
4297 		int temp;
4298 
4299 		if (CUIDialog::windowGetValue(hSaveDialog, IDC_SAVE_WIDTH, temp) &&
4300 			temp > 0 && temp <= MAX_SNAPSHOT_WIDTH)
4301 		{
4302 			saveWidth = temp;
4303 		}
4304 		else
4305 		{
4306 			CUIDialog::windowSetValue(hSaveDialog, IDC_SAVE_WIDTH, saveWidth);
4307 			MessageBeep(MB_OK);
4308 		}
4309 	}
4310 }
4311 
updateSaveHeight(void)4312 void ModelWindow::updateSaveHeight(void)
4313 {
4314 	if (!saveActualSize)
4315 	{
4316 		int temp;
4317 
4318 		if (CUIDialog::windowGetValue(hSaveDialog, IDC_SAVE_HEIGHT, temp) &&
4319 			temp > 0 && temp <= MAX_SNAPSHOT_HEIGHT)
4320 		{
4321 			saveHeight = temp;
4322 		}
4323 		else
4324 		{
4325 			CUIDialog::windowSetValue(hSaveDialog, IDC_SAVE_HEIGHT, saveHeight);
4326 			MessageBeep(MB_OK);
4327 		}
4328 	}
4329 }
4330 
updateStepSuffix(void)4331 void ModelWindow::updateStepSuffix(void)
4332 {
4333 	if (saveAllSteps)
4334 	{
4335 		ucstring newSuffix;
4336 
4337 		CUIDialog::windowGetText(hSaveDialog, IDC_STEP_SUFFIX, newSuffix);
4338 		delete saveStepSuffix;
4339 		saveStepSuffix = copyString(newSuffix.c_str());
4340 	}
4341 	updateSaveFilename();
4342 }
4343 
updateSaveFilename(void)4344 void ModelWindow::updateSaveFilename(void)
4345 {
4346 	UCCHAR buf[1024];
4347 
4348 	if (calcSaveFilename(buf, 1024))
4349 	{
4350 		SendMessage(GetParent(hSaveDialog), CDM_SETCONTROLTEXT, edt1,
4351 			(LPARAM)buf);
4352 	}
4353 }
4354 
updateSaveDigits(void)4355 void ModelWindow::updateSaveDigits(void)
4356 {
4357 	int temp;
4358 
4359 	if (CUIDialog::windowGetValue(hSaveDialog, IDC_SAVE_DIGITS, temp) &&
4360 		temp > 0 && temp <= 6)
4361 	{
4362 		saveDigits = temp;
4363 	}
4364 	updateSaveFilename();
4365 }
4366 
doSaveOptionsClick(void)4367 void ModelWindow::doSaveOptionsClick(void)
4368 {
4369 	switch (curSaveOp)
4370 	{
4371 	case LDPreferences::SOExport:
4372 		{
4373 			LDExporter *exporter = modelViewer->getExporter();
4374 
4375 			if (exporter != NULL)
4376 			{
4377 				ExportOptionsDialog *dialog = new ExportOptionsDialog(
4378 					getLanguageModule(), exporter);
4379 
4380 				if (dialog->doModal(hSaveDialog) != IDOK)
4381 				{
4382 					// Force a new exporter to be created in order to forget
4383 					// about any settings changes that might have happened as
4384 					// a result of a reset.
4385 					modelViewer->getExporter((LDrawModelViewer::ExportType)0,
4386 						true);
4387 				}
4388 				dialog->release();
4389 			}
4390 		}
4391 		break;
4392 	case LDPreferences::SOSnapshot:
4393 	default:
4394 		{
4395 			JpegOptionsDialog *dialog = new JpegOptionsDialog(
4396 				getLanguageModule(), hSaveDialog);
4397 
4398 			dialog->doModal(hSaveDialog);
4399 			dialog->release();
4400 		}
4401 		break;
4402 	}
4403 }
4404 
doSaveClick(int controlId,HWND)4405 BOOL ModelWindow::doSaveClick(int controlId, HWND /*hControlWnd*/)
4406 {
4407 	switch (controlId)
4408 	{
4409 	case IDC_SAVE_OPTIONS:
4410 		doSaveOptionsClick();
4411 		break;
4412 	case IDC_SAVE_ACTUAL_SIZE:
4413 		saveActualSize = !CUIDialog::buttonGetCheck(hSaveDialog, controlId);
4414 		updateSaveSizeEnabled();
4415 		updateSaveAllStepsEnabled();
4416 		break;
4417 	case IDC_SAVE_SERIES:
4418 		saveSeries = CUIDialog::buttonGetCheck(hSaveDialog, controlId);
4419 		updateSaveSeriesEnabled();
4420 		break;
4421 	case IDC_ALL_STEPS:
4422 		saveAllSteps = CUIDialog::buttonGetCheck(hSaveDialog, controlId);
4423 		updateSaveAllStepsEnabled();
4424 		break;
4425 	case IDC_IGNORE_PBUFFER:
4426 		ignorePBuffer = CUIDialog::buttonGetCheck(hSaveDialog, controlId);
4427 		break;
4428 	case IDC_IGNORE_FBO:
4429 		ignoreFBO = CUIDialog::buttonGetCheck(hSaveDialog, controlId);
4430 		break;
4431 	case IDC_IGNORE_PIXEL_FORMAT:
4432 		ignorePixelFormat = CUIDialog::buttonGetCheck(hSaveDialog, controlId);
4433 		break;
4434 	case IDC_TRANSPARENT_BACKGROUND:
4435 		saveAlpha = CUIDialog::buttonGetCheck(hSaveDialog, controlId);
4436 		break;
4437 	case IDC_AUTO_CROP:
4438 		autoCrop = CUIDialog::buttonGetCheck(hSaveDialog, controlId);
4439 		break;
4440 	case IDC_SAME_SCALE:
4441 		saveStepsSameScale = CUIDialog::buttonGetCheck(hSaveDialog, controlId);
4442 		break;
4443 	case IDC_SAVE_ZOOMTOFIT:
4444 		saveZoomToFit = CUIDialog::buttonGetCheck(hSaveDialog, controlId);
4445 		updateSaveAllStepsEnabled();
4446 		break;
4447 	default:
4448 		return FALSE;
4449 	}
4450 	return TRUE;
4451 }
4452 
doSaveKillFocus(int controlId,HWND)4453 BOOL ModelWindow::doSaveKillFocus(int controlId, HWND /*hControlWnd*/)
4454 {
4455 	switch (controlId)
4456 	{
4457 	case IDC_SAVE_WIDTH:
4458 		updateSaveWidth();
4459 		break;
4460 	case IDC_SAVE_HEIGHT:
4461 		updateSaveHeight();
4462 		break;
4463 	case IDC_SAVE_DIGITS:
4464 		updateSaveDigits();
4465 		break;
4466 	default:
4467 		return FALSE;
4468 	}
4469 	return TRUE;
4470 }
4471 
doSaveNotify(int,LPOFNOTIFY notification)4472 BOOL ModelWindow::doSaveNotify(int /*controlId*/, LPOFNOTIFY notification)
4473 {
4474 	switch(notification->hdr.code)
4475 	{
4476 	case CDN_TYPECHANGE:
4477 		{
4478 			UCCHAR buf[1024];
4479 
4480 			saveType = notification->lpOFN->nFilterIndex;
4481 			switch (curSaveOp)
4482 			{
4483 			case LDPreferences::SOExport:
4484 				saveExportType = saveType;
4485 				break;
4486 			case LDPreferences::SOSnapshot:
4487 			default:
4488 				saveImageType = saveType;
4489 				break;
4490 			}
4491 			if (calcSaveFilename(buf, 1024))
4492 			{
4493 				SendMessage(GetParent(hSaveDialog), CDM_SETCONTROLTEXT, edt1,
4494 					(LPARAM)buf);
4495 			}
4496 			saveTypeUpdated();
4497 		}
4498 		break;
4499 	case CDN_INITDONE:
4500 		return doSaveInitDone(notification);
4501 		break;
4502 	default:
4503 //		debugPrintf("%d\n", notification->hdr.code);
4504 		break;
4505 	}
4506 	return FALSE;
4507 }
4508 
doSaveCommand(int controlId,int notifyCode,HWND hControlWnd)4509 BOOL ModelWindow::doSaveCommand(int controlId, int notifyCode, HWND hControlWnd)
4510 {
4511 //	debugPrintf("ModelWindow::doSaveCommand: 0x%04X, 0x%04X, 0x%04x\n",
4512 //		controlId, notifyCode, hControlWnd);
4513 	switch (notifyCode)
4514 	{
4515 	case BN_CLICKED:
4516 		return doSaveClick(controlId, hControlWnd);
4517 		break;
4518 	case EN_KILLFOCUS:
4519 		return doSaveKillFocus(controlId, hControlWnd);
4520 		break;
4521 	case EN_CHANGE:
4522 		switch (controlId)
4523 		{
4524 		case IDC_SAVE_DIGITS:
4525 			updateSaveDigits();
4526 			break;
4527 		case IDC_STEP_SUFFIX:
4528 			updateStepSuffix();
4529 			break;
4530 		}
4531 		break;
4532 	}
4533 	return FALSE;
4534 }
4535 
doPrintClick(int controlId,HWND)4536 BOOL ModelWindow::doPrintClick(int controlId, HWND /*hControlWnd*/)
4537 {
4538 	switch (controlId)
4539 	{
4540 	case IDC_PRINT_PRINTER_DPI:
4541 		usePrinterDPI = true;
4542 		updatePrintDPIField();
4543 		break;
4544 	case IDC_PRINT_SPECIFY_DPI:
4545 		usePrinterDPI = false;
4546 		updatePrintDPIField();
4547 		break;
4548 	case IDC_PRINT_BACKGROUND:
4549 		printBackground = CUIDialog::buttonGetCheck(hPrintDialog, controlId);
4550 		break;
4551 	default:
4552 		return FALSE;
4553 	}
4554 	return TRUE;
4555 }
4556 
updatePrintDPI(void)4557 void ModelWindow::updatePrintDPI(void)
4558 {
4559 	int temp;
4560 
4561 	if (CUIDialog::windowGetValue(hPrintDialog, IDC_PRINT_DPI, temp) && temp > 0
4562 		&& temp <= 2400)
4563 	{
4564 		printDPI = temp;
4565 	}
4566 	else
4567 	{
4568 		CUIDialog::windowSetValue(hSaveDialog, IDC_PRINT_DPI, printDPI);
4569 		MessageBeep(MB_OK);
4570 	}
4571 }
4572 
doPrintKillFocus(int controlId,HWND)4573 BOOL ModelWindow::doPrintKillFocus(int controlId, HWND /*hControlWnd*/)
4574 {
4575 	switch (controlId)
4576 	{
4577 	case IDC_PRINT_DPI:
4578 		updatePrintDPI();
4579 		break;
4580 	default:
4581 		return FALSE;
4582 	}
4583 	return TRUE;
4584 }
doPrintCommand(int controlId,int notifyCode,HWND hControlWnd)4585 BOOL ModelWindow::doPrintCommand(int controlId, int notifyCode,
4586 								 HWND hControlWnd)
4587 {
4588 	switch (notifyCode)
4589 	{
4590 	case BN_CLICKED:
4591 		return doPrintClick(controlId, hControlWnd);
4592 		break;
4593 	case EN_KILLFOCUS:
4594 		return doPrintKillFocus(controlId, hControlWnd);
4595 		break;
4596 	}
4597 	return FALSE;
4598 }
4599 
staticSaveHook(HWND hDlg,UINT uiMsg,WPARAM wParam,LPARAM lParam)4600 UINT_PTR CALLBACK ModelWindow::staticSaveHook(
4601 	HWND hDlg,
4602 	UINT uiMsg,
4603 	WPARAM wParam,
4604 	LPARAM lParam)
4605 {
4606 	ModelWindow* modelWindow = (ModelWindow*)GetWindowLongPtr(hDlg, GWLP_USERDATA);
4607 
4608 	if (uiMsg == WM_INITDIALOG)
4609 	{
4610 		modelWindow = (ModelWindow*)((OPENFILENAME*)lParam)->lCustData;
4611 		if (modelWindow)
4612 		{
4613 			modelWindow->hSaveDialog = hDlg;
4614 			SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)modelWindow);
4615 			// Add the context help button to the OpenFilename window.
4616 			HWND hOpen = GetParent(hDlg);
4617 			long styleEx = GetWindowLong(hOpen, GWL_EXSTYLE);
4618 			styleEx = styleEx | WS_EX_CONTEXTHELP;
4619 			SetWindowLong(hOpen, GWL_EXSTYLE, styleEx);
4620 		}
4621 	}
4622 	else if (uiMsg == WM_HELP)
4623 	{
4624 		return CUIDialog::DoHtmlHelp(hDlg, (LPHELPINFO)lParam);
4625 	}
4626 	if (modelWindow)
4627 	{
4628 		return modelWindow->dialogProc(hDlg, uiMsg, wParam, lParam);
4629 	}
4630 	else
4631 	{
4632 		return 0;
4633 	}
4634 }
4635 
saveExtension(int type) const4636 CUCSTR ModelWindow::saveExtension(int type /*= -1*/) const
4637 {
4638 	if (type == -1)
4639 	{
4640 		type = saveImageType;
4641 	}
4642 	switch (type)
4643 	{
4644 	case BMP_IMAGE_TYPE_INDEX:
4645 		return _UC("bmp");
4646 	case JPG_IMAGE_TYPE_INDEX:
4647 		return _UC("jpg");
4648 	case SVG_IMAGE_TYPE_INDEX:
4649 		return _UC("svg");
4650 	case EPS_IMAGE_TYPE_INDEX:
4651 		return _UC("eps");
4652 	case PDF_IMAGE_TYPE_INDEX:
4653 		return _UC("pdf");
4654 	default:
4655 		return _UC("png");
4656 	}
4657 }
4658 
calcSaveFilename(UCSTR saveFilename,int len)4659 bool ModelWindow::calcSaveFilename(
4660 	UCSTR saveFilename,
4661 	int len)
4662 {
4663 	char* filename = filenameFromPath(modelViewer->getFilename());
4664 
4665 	if (filename != NULL)
4666 	{
4667 		bool found = false;
4668 		std::string baseFilename = filename;
4669 		size_t dotSpot;
4670 		LDLModel *mpdChild = modelViewer->getMpdChild();
4671 
4672 		delete[] filename;
4673 		dotSpot = baseFilename.rfind('.');
4674 		if (dotSpot < baseFilename.size())
4675 		{
4676 			baseFilename.erase(dotSpot);
4677 		}
4678 		if (mpdChild != NULL && mpdChild->getName() != NULL &&
4679 			modelViewer->getMpdChildIndex() > 0)
4680 		{
4681 			std::string mpdName = mpdChild->getName();
4682 
4683 			dotSpot = mpdName.rfind('.');
4684 			if (dotSpot < mpdName.size())
4685 			{
4686 				std::string mpdExt = mpdName.substr(dotSpot);
4687 
4688 				convertStringToLower(&mpdExt[0]);
4689 				if (mpdExt == ".dat" || mpdExt == ".ldr" || mpdExt == ".mpd")
4690 				{
4691 					mpdName = mpdName.substr(0, dotSpot);
4692 				}
4693 			}
4694 			baseFilename += '-';
4695 			baseFilename += mpdName;
4696 		}
4697 		ucstring ucBaseFilename;
4698 		utf8toucstring(ucBaseFilename, baseFilename.c_str());
4699 		if (curSaveOp == LDPreferences::SOExport)
4700 		{
4701 			LDExporter *exporter = modelViewer->getExporter(
4702 				(LDrawModelViewer::ExportType)saveExportType);
4703 			std::string extension;
4704 
4705 			if (exporter)
4706 			{
4707 				extension = exporter->getExtension();
4708 			}
4709 			else
4710 			{
4711 				extension = "pov";
4712 			}
4713 			ucstring ucExtension;
4714 			utf8toucstring(ucExtension, extension.c_str());
4715 			sucprintf(saveFilename, len, _UC("%s.%s"), ucBaseFilename.c_str(),
4716 				ucExtension.c_str());
4717 			return true;
4718 		}
4719 		else
4720 		{
4721 			CUCSTR extension = saveExtension();
4722 			UCCHAR format[32] = _UC("%s.%s");
4723 			int max;
4724 			int i;
4725 
4726 			if (saveSeries)
4727 			{
4728 				sucprintf(format, COUNT_OF(format), _UC("%%s%%0%dd.%%s"), saveDigits);
4729 				max = (int)(pow(10.0, saveDigits) + 0.1);
4730 			}
4731 			else
4732 			{
4733 				max = 2;
4734 			}
4735 			std::string utf8SaveFilename;
4736 			for (i = 1; i < max && !found; i++)
4737 			{
4738 				if (saveSeries)
4739 				{
4740 					sucprintf(saveFilename, len, format, ucBaseFilename.c_str(), i,
4741 						extension);
4742 				}
4743 				else
4744 				{
4745 					sucprintf(saveFilename, len, format, ucBaseFilename.c_str(), extension);
4746 				}
4747 				if (saveAllSteps)
4748 				{
4749 					std::string suffix;
4750 					ucstringtoutf8(suffix, saveStepSuffix);
4751 					ucstringtoutf8(utf8SaveFilename, saveFilename);
4752 					std::string temp = LDSnapshotTaker::addStepSuffix(utf8SaveFilename,
4753 						suffix, 1, modelViewer->getNumSteps());
4754 					ucstring ucTemp;
4755 					utf8toucstring(ucTemp, temp.c_str());
4756 					ucstrcpy(saveFilename, ucTemp.c_str());
4757 				}
4758 				utf8SaveFilename = ucstringtoutf8(saveFilename);
4759 				if (!LDrawModelViewer::fileExists(utf8SaveFilename.c_str()))
4760 				{
4761 					found = true;
4762 				}
4763 			}
4764 			return true;
4765 		}
4766 	}
4767 	else
4768 	{
4769 		return false;
4770 	}
4771 }
4772 
4773 // Note: static method
defaultDir(LDPreferences::DefaultDirMode mode,const char * lastPath,const char * defaultPath)4774 std::string ModelWindow::defaultDir(
4775 	LDPreferences::DefaultDirMode mode,
4776 	const char *lastPath,
4777 	const char *defaultPath)
4778 {
4779 	switch (mode)
4780 	{
4781 	case LDPreferences::DDMLastDir:
4782 		{
4783 			return lastPath;
4784 		}
4785 	case LDPreferences::DDMSpecificDir:
4786 		return defaultPath;
4787 	}
4788 	return ".";
4789 }
4790 
getSaveDir(LDPreferences::SaveOp saveOp)4791 std::string ModelWindow::getSaveDir(LDPreferences::SaveOp saveOp)
4792 {
4793 	LDPreferences *ldPrefs = prefs->getLDPrefs();
4794 
4795 	if (ldPrefs != NULL)
4796 	{
4797 		return ldPrefs->getDefaultSaveDir(saveOp, modelViewer->getFilename());
4798 	}
4799 	return ".";
4800 }
4801 
getSaveDir(void)4802 std::string ModelWindow::getSaveDir(void)
4803 {
4804 	return getSaveDir(curSaveOp);
4805 }
4806 
fillSnapshotFileTypes(UCSTR fileTypes)4807 void ModelWindow::fillSnapshotFileTypes(UCSTR fileTypes)
4808 {
4809 	memset(fileTypes, 0, 2 * sizeof(fileTypes[0]));
4810 	addFileType(fileTypes, ls(_UC("PngFileType")), _UC("*.png"));
4811 	addFileType(fileTypes, ls(_UC("BmpFileType")), _UC("*.bmp"));
4812 	addFileType(fileTypes, ls(_UC("JpgFileType")), _UC("*.jpg"));
4813 	if (TCUserDefaults::boolForKey(GL2PS_ALLOWED_KEY, false, false))
4814 	{
4815 		addFileType(fileTypes, ls(_UC("SvgFileType")), _UC("*.svg"));
4816 		addFileType(fileTypes, ls(_UC("EpsFileType")), _UC("*.eps"));
4817 		addFileType(fileTypes, ls(_UC("PdfFileType")), _UC("*.pdf"));
4818 	}
4819 }
4820 
fillExportFileTypes(UCSTR fileTypes)4821 void ModelWindow::fillExportFileTypes(UCSTR fileTypes)
4822 {
4823 	memset(fileTypes, 0, 2 * sizeof(fileTypes[0]));
4824 	for (int i = LDrawModelViewer::ETFirst; i <= LDrawModelViewer::ETLast; i++)
4825 	{
4826 		const LDExporter *exporter = modelViewer->getExporter(
4827 			(LDrawModelViewer::ExportType)i);
4828 
4829 		if (exporter != NULL)
4830 		{
4831 			ucstring fileType = exporter->getTypeDescription();
4832 			ucstring extension = _UC("*.");
4833 			std::string exporterExtension = exporter->getExtension();
4834 			ucstring ucExporterExtension;
4835 			utf8toucstring(ucExporterExtension, exporterExtension);
4836 
4837 			extension += ucExporterExtension;
4838 			addFileType(fileTypes, fileType.c_str(), extension.c_str());
4839 		}
4840 	}
4841 }
4842 
getSaveFilename(UCSTR saveFilename,int len)4843 bool ModelWindow::getSaveFilename(
4844 	UCSTR saveFilename,
4845 	int len)
4846 {
4847 	OPENFILENAME openStruct;
4848 	UCCHAR fileTypes[1024];
4849 	std::string initialDir = getSaveDir();
4850 	int maxImageType = 3;
4851 	ucstring defaultExt;
4852 
4853 	stopAnimation();
4854 	memset(&openStruct, 0, sizeof(OPENFILENAME));
4855 	if (!calcSaveFilename(saveFilename, len))
4856 	{
4857 		saveFilename[0] = 0;
4858 	}
4859 	openStruct.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT |
4860 		OFN_ENABLESIZING;
4861 	switch (curSaveOp)
4862 	{
4863 	case LDPreferences::SOExport:
4864 		{
4865 			const LDExporter *exporter = modelViewer->getExporter();
4866 			std::string extension = exporter->getExtension();
4867 
4868 			fillExportFileTypes(fileTypes);
4869 			modelViewer->setExportType(
4870 				(LDrawModelViewer::ExportType)saveExportType);
4871 			saveType = saveExportType;
4872 			openStruct.lpstrTitle = ls(_UC("ExportModel"));
4873 			openStruct.Flags |= OFN_ENABLETEMPLATE | OFN_ENABLEHOOK;
4874 			openStruct.hInstance = getLanguageModule();
4875 			openStruct.lpTemplateName = MAKEINTRESOURCE(IDD_EXPORT_SAVE_OPTIONS);
4876 			openStruct.lpfnHook = staticSaveHook;
4877 			utf8toucstring(defaultExt, extension);
4878 		}
4879 		break;
4880 	case LDPreferences::SOSnapshot:
4881 	default:
4882 		fillSnapshotFileTypes(fileTypes);
4883 		if (saveImageType < 1 || saveImageType > maxImageType)
4884 		{
4885 			saveImageType = 1;
4886 		}
4887 		saveType = saveImageType;
4888 		openStruct.lpstrTitle = ls(_UC("SaveSnapshot"));
4889 		openStruct.Flags |= OFN_ENABLETEMPLATE | OFN_ENABLEHOOK;
4890 		openStruct.hInstance = getLanguageModule();
4891 		openStruct.lpTemplateName = MAKEINTRESOURCE(IDD_SAVE_OPTIONS);
4892 		openStruct.lpfnHook = staticSaveHook;
4893 		defaultExt = saveExtension();
4894 		break;
4895 	}
4896 	openStruct.lStructSize = getOpenFilenameSize(false);
4897 	openStruct.hwndOwner = hWindow;
4898 	openStruct.lpstrFilter = fileTypes;
4899 	openStruct.nFilterIndex = saveType;
4900 	openStruct.lpstrFile = saveFilename;
4901 	openStruct.lpstrDefExt = defaultExt.c_str();
4902 	openStruct.nMaxFile = len;
4903 	ucstring ucInitialDir;
4904 	utf8toucstring(ucInitialDir, initialDir);
4905 	openStruct.lpstrInitialDir = ucInitialDir.c_str();
4906 	openStruct.lCustData = (LPARAM)this;
4907 	if (GetSaveFileName(&openStruct))
4908 	{
4909 		int index = (int)openStruct.nFilterIndex;
4910 		ucstring dir = directoryFromPath(saveFilename);
4911 		LDPreferences *ldPrefs = prefs->getLDPrefs();
4912 
4913 		if (ldPrefs != NULL)
4914 		{
4915 			std::string utf8Dir;
4916 			ucstringtoutf8(utf8Dir, dir);
4917 			ldPrefs->setLastSaveDir(curSaveOp, utf8Dir.c_str(), true);
4918 		}
4919 		switch (curSaveOp)
4920 		{
4921 		case LDPreferences::SOExport:
4922 			TCUserDefaults::setLongForKey(saveExportType,
4923 				SAVE_EXPORT_TYPE_KEY, false);
4924 			break;
4925 		case LDPreferences::SOSnapshot:
4926 		default:
4927 			if (index > 0 && index <= maxImageType)
4928 			{
4929 				TCUserDefaults::setLongForKey(saveImageType,
4930 					SAVE_IMAGE_TYPE_KEY, false);
4931 			}
4932 			break;
4933 		}
4934 		TCObject::release(saveWindowResizer);
4935 		saveWindowResizer = NULL;
4936 		TCUserDefaults::setBoolForKey(saveAlpha, SAVE_ALPHA_KEY, false);
4937 		TCUserDefaults::setBoolForKey(autoCrop, AUTO_CROP_KEY, false);
4938 		TCUserDefaults::setBoolForKey(saveAllSteps, SAVE_STEPS_KEY, false);
4939 		if (saveAllSteps)
4940 		{
4941 			TCUserDefaults::setStringForKey(saveStepSuffix,
4942 				SAVE_STEPS_SUFFIX_KEY, false);
4943 			TCUserDefaults::setBoolForKey(saveStepsSameScale,
4944 				SAVE_STEPS_SAME_SCALE_KEY, false);
4945 		}
4946 		return true;
4947 	}
4948 	else
4949 	{
4950 		loadSaveSettings();
4951 	}
4952 	TCObject::release(saveWindowResizer);
4953 	saveWindowResizer = NULL;
4954 	return false;
4955 }
4956 
exportModel(void)4957 void ModelWindow::exportModel(void)
4958 {
4959 	UCCHAR filename[1024];
4960 
4961 	curSaveOp = LDPreferences::SOExport;
4962 	if (getSaveFilename(filename, COUNT_OF(filename)))
4963 	{
4964 		modelViewer->setExportType(
4965 			(LDrawModelViewer::ExportType)saveExportType);
4966 		setWaitCursor();
4967 		std::string utf8Filename;
4968 		ucstringtoutf8(utf8Filename, filename);
4969 		modelViewer->exportCurModel(utf8Filename.c_str(),
4970 			LDViewWindow::getAppVersion().c_str(),
4971 			LDViewWindow::getAppAsciiCopyright().c_str());
4972 		setArrowCursor();
4973 	}
4974 }
4975 
saveSnapshot(void)4976 bool ModelWindow::saveSnapshot(void)
4977 {
4978 	UCCHAR saveFilename[1024] = _UC("");
4979 	bool retValue = saveSnapshot(saveFilename);
4980 
4981 	forceRedraw();
4982 	return retValue;
4983 }
4984 
saveSnapshot(UCSTR saveFilename,bool fromCommandLine,bool notReallyCommandLine)4985 bool ModelWindow::saveSnapshot(UCSTR saveFilename, bool fromCommandLine,
4986 							   bool notReallyCommandLine)
4987 {
4988 	bool externalFilename = saveFilename[0] != 0;
4989 
4990 	curSaveOp = LDPreferences::SOSnapshot;
4991 	savingFromCommandLine = fromCommandLine && !notReallyCommandLine;
4992 	if (saveFilename[0])
4993 	{
4994 		UCSTR snapshotSuffixTemp =
4995 			TCUserDefaults::stringForKeyUC(SNAPSHOT_SUFFIX_KEY, NULL, false);
4996 		ucstring snapshotSuffix(snapshotSuffixTemp ? snapshotSuffixTemp : _UC(""));
4997 		delete[] snapshotSuffixTemp;
4998 
4999 		if (snapshotSuffix.empty())
5000 		{
5001 			UCCHAR *suffixSpot = ucstrrchr(saveFilename, _UC('.'));
5002 
5003 			if (suffixSpot != NULL)
5004 			{
5005 				snapshotSuffix = suffixSpot;
5006 			}
5007 		}
5008 		if (stringHasCaseInsensitiveSuffix(snapshotSuffix.c_str(), _UC(".png")))
5009 		{
5010 			saveImageType = PNG_IMAGE_TYPE_INDEX;
5011 		}
5012 		else if (stringHasCaseInsensitiveSuffix(snapshotSuffix.c_str(),
5013 			_UC(".bmp")))
5014 		{
5015 			saveImageType = BMP_IMAGE_TYPE_INDEX;
5016 		}
5017 		else if (stringHasCaseInsensitiveSuffix(snapshotSuffix.c_str(),
5018 			_UC(".jpg")))
5019 		{
5020 			saveImageType = JPG_IMAGE_TYPE_INDEX;
5021 		}
5022 		else
5023 		{
5024 			if (fromCommandLine)
5025 			{
5026 				consolePrintf(ls(_UC("ConsoleSnapshotFailed")));
5027 			}
5028 			return false;
5029 		}
5030 	}
5031 	if (saveFilename[0] || getSaveFilename(saveFilename, 1024))
5032 	{
5033 		if (!fromCommandLine)
5034 		{
5035 			TCUserDefaults::setLongForKey(ignorePBuffer ? 1 : 0,
5036 				IGNORE_PBUFFER_KEY, false);
5037 			TCUserDefaults::setLongForKey(ignoreFBO ? 1 : 0,
5038 				IGNORE_FRAMEBUFFER_OBJECT_KEY, false);
5039 			TCUserDefaults::setLongForKey(ignorePixelFormat ? 1 : 0,
5040 				IGNORE_PIXEL_FORMAT_KEY, false);
5041 			if (saveSeries)
5042 			{
5043 				TCUserDefaults::setLongForKey(1, SAVE_SERIES_KEY, false);
5044 				TCUserDefaults::setLongForKey(saveDigits, SAVE_DIGITS_KEY,
5045 					false);
5046 			}
5047 			else
5048 			{
5049 				TCUserDefaults::setLongForKey(0, SAVE_SERIES_KEY, false);
5050 			}
5051 		}
5052 		if (saveActualSize)
5053 		{
5054 			if (!fromCommandLine)
5055 			{
5056 				TCUserDefaults::setLongForKey(1, SAVE_ACTUAL_SIZE_KEY, false);
5057 			}
5058 			return saveImage(saveFilename, width, height, externalFilename &&
5059 				saveZoomToFit);
5060 		}
5061 		else
5062 		{
5063 			if (!fromCommandLine)
5064 			{
5065 				TCUserDefaults::setLongForKey(0, SAVE_ACTUAL_SIZE_KEY, false);
5066 				TCUserDefaults::setLongForKey(saveWidth, SAVE_WIDTH_KEY, false);
5067 				TCUserDefaults::setLongForKey(saveHeight, SAVE_HEIGHT_KEY,
5068 					false);
5069 				TCUserDefaults::setLongForKey(saveZoomToFit,
5070 					SAVE_ZOOM_TO_FIT_KEY, false);
5071 			}
5072 			return saveImage(saveFilename, saveWidth, saveHeight,
5073 				saveZoomToFit);
5074 		}
5075 	}
5076 	else
5077 	{
5078 		return false;
5079 	}
5080 }
5081 
setupPFD(void)5082 BOOL ModelWindow::setupPFD(void)
5083 {
5084 	currentAntialiasType = TCUserDefaults::longForKey(FSAA_MODE_KEY);
5085 
5086 	if (currentAntialiasType && LDVExtensionsSetup::haveMultisampleExtension())
5087 	{
5088 		debugOut("ModelWindow::setupPFD 1\n");
5089 		GLint intValues[] = {
5090 			WGL_SAMPLE_BUFFERS_EXT, GL_TRUE,
5091 			WGL_SAMPLES_EXT, 1,
5092 			WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
5093 			WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
5094 			WGL_ALPHA_BITS_ARB, 4,
5095 			WGL_STENCIL_BITS_ARB, 2,
5096 			0, 0
5097 		};
5098 		int numIntValues = COUNT_OF(intValues);
5099 
5100 		if (currentAntialiasType)
5101 		{
5102 			intValues[3] = prefs->getFSAAFactor();
5103 		}
5104 		else
5105 		{
5106 			// Remove the first four items in the array.
5107 			memmove(intValues, intValues + 4, sizeof(intValues) -
5108 				sizeof(intValues[0]) * 4);
5109 			numIntValues -= 4;
5110 		}
5111 		pfIndex = LDVExtensionsSetup::choosePixelFormat(hdc, intValues);
5112 		debugOut("ModelWindow::setupPFD 2\n");
5113 		if (pfIndex < 0)
5114 		{
5115 			// Try with dest alpha but no stencil by clearing the last two used
5116 			// elements in the array (there are two terminating zeros after
5117 			// them).
5118 			intValues[numIntValues - 4] = 0;
5119 			intValues[numIntValues - 3] = 0;
5120 			pfIndex = LDVExtensionsSetup::choosePixelFormat(hdc, intValues);
5121 		}
5122 		debugOut("ModelWindow::setupPFD 3\n");
5123 		if (pfIndex < 0)
5124 		{
5125 			// Try with stencil but no dest alpha by changing the last two used
5126 			// elements in the array back to their original values that specify
5127 			// the stencil request.
5128 			intValues[numIntValues - 6] = WGL_STENCIL_BITS_ARB;
5129 			intValues[numIntValues - 5] = 1;
5130 			pfIndex = LDVExtensionsSetup::choosePixelFormat(hdc, intValues);
5131 		}
5132 		debugOut("ModelWindow::setupPFD 4\n");
5133 		if (pfIndex < 0)
5134 		{
5135 			// Try with no stencil OR dest alpha by clearing two more elements
5136 			// at the end of the array.
5137 			intValues[numIntValues - 6] = 0;
5138 			intValues[numIntValues - 5] = 0;
5139 			pfIndex = LDVExtensionsSetup::choosePixelFormat(hdc, intValues);
5140 		}
5141 		debugOut("ModelWindow::setupPFD 5\n");
5142 		if (pfIndex >= 0)
5143 		{
5144 			debugOut("ModelWindow::setupPFD 6\n");
5145 			if (setPixelFormat(pfIndex))
5146 			{
5147 				return TRUE;
5148 			}
5149 		}
5150 		debugOut("ModelWindow::setupPFD 7\n");
5151 	}
5152 	currentAntialiasType = 0;
5153 	return CUIOGLWindow::setupPFD();
5154 }
5155 
setKeepRightSideUp(bool value,bool saveSetting)5156 void ModelWindow::setKeepRightSideUp(bool value, bool saveSetting /*= true*/)
5157 {
5158 	if (modelViewer != NULL)
5159 	{
5160 		modelViewer->setKeepRightSideUp(value);
5161 	}
5162 	if (saveSetting)
5163 	{
5164 		TCUserDefaults::setBoolForKey(value, KEEP_RIGHT_SIDE_UP_KEY, false);
5165 	}
5166 }
5167 
setViewMode(LDInputHandler::ViewMode mode,bool examineLatLong,bool saveSetting)5168 void ModelWindow::setViewMode(
5169 	LDInputHandler::ViewMode mode,
5170 	bool examineLatLong,
5171 	bool saveSetting)
5172 {
5173 	inputHandler->setViewMode(mode);
5174 	viewMode = mode;
5175 	if (saveSetting)
5176 	{
5177 		TCUserDefaults::setLongForKey(viewMode, VIEW_MODE_KEY, false);
5178 	}
5179 	if (modelViewer)
5180 	{
5181 		if (viewMode == LDInputHandler::VMExamine)
5182 		{
5183 			LDrawModelViewer::ExamineMode examineMode;
5184 
5185 			modelViewer->setConstrainZoom(true);
5186 			if (examineLatLong)
5187 			{
5188 				examineMode = LDrawModelViewer::EMLatLong;
5189 			}
5190 			else
5191 			{
5192 				examineMode = LDrawModelViewer::EMFree;
5193 			}
5194 			if (saveSetting)
5195 			{
5196 				TCUserDefaults::setLongForKey(examineMode, EXAMINE_MODE_KEY,
5197 					false);
5198 			}
5199 			modelViewer->setExamineMode(examineMode);
5200 		}
5201 		else
5202 		{
5203 			modelViewer->setConstrainZoom(false);
5204 		}
5205 	}
5206 }
5207 
processKeyDown(int keyCode,LPARAM)5208 LRESULT ModelWindow::processKeyDown(int keyCode, LPARAM /*keyData*/)
5209 {
5210 	if (keyCode == VK_ESCAPE)
5211 	{
5212 		cancelLoad = true;
5213 	}
5214 	if (inputHandler->keyDown(getCurrentKeyModifiers(),
5215 		convertKeyCode(keyCode)))
5216 	{
5217 		return 0;
5218 	}
5219 	return 1;
5220 	//if (modelViewer && viewMode == LDVViewFlythrough)
5221 	//{
5222 	//	TCVector cameraMotion = modelViewer->getCameraMotion();
5223 	//	TCFloat motionAmount = 1.0f;
5224 	//	TCFloat rotationAmount = 0.01f;
5225 	//	TCFloat strafeAmount = 1.0f;
5226 	//	int i;
5227 
5228 	//	if (modelViewer)
5229 	//	{
5230 	//		TCFloat fov = modelViewer->getFov();
5231 
5232 	//		motionAmount = modelViewer->getDefaultDistance() / 400.0f;
5233 	//		rotationAmount *= (TCFloat)tan(deg2rad(fov));
5234 	//		strafeAmount *= (TCFloat)sqrt(fov / 45.0f);
5235 	//	}
5236 	//	if (GetKeyState(VK_SHIFT) & 0x8000)
5237 	//	{
5238 	//		motionAmount *= 2.0f;
5239 	//		strafeAmount *= 2.0f;
5240 	//	}
5241 	//	switch (keyCode)
5242 	//	{
5243 	//	case 'W':
5244 	//		cameraMotion[2] = -motionAmount;
5245 	//		break;
5246 	//	case 'S':
5247 	//		cameraMotion[2] = motionAmount;
5248 	//		break;
5249 	//	case 'A':
5250 	//		cameraMotion[0] = -strafeAmount;
5251 	//		break;
5252 	//	case 'D':
5253 	//		cameraMotion[0] = strafeAmount;
5254 	//		break;
5255 	//	case 'R':
5256 	//		cameraMotion[1] = strafeAmount;
5257 	//		break;
5258 	//	case 'F':
5259 	//		cameraMotion[1] = -strafeAmount;
5260 	//		break;
5261 	//	case 'E':
5262 	//		modelViewer->setCameraZRotate(0.01f);
5263 	//		break;
5264 	//	case 'Q':
5265 	//		modelViewer->setCameraZRotate(-0.01f);
5266 	//		break;
5267 	//	case VK_UP:
5268 	//		modelViewer->setCameraYRotate(rotationAmount);
5269 	//		break;
5270 	//	case VK_DOWN:
5271 	//		modelViewer->setCameraYRotate(-rotationAmount);
5272 	//		break;
5273 	//	case VK_LEFT:
5274 	//		modelViewer->setCameraXRotate(-rotationAmount);
5275 	//		break;
5276 	//	case VK_RIGHT:
5277 	//		modelViewer->setCameraXRotate(rotationAmount);
5278 	//		break;
5279 	//	case VK_SHIFT:
5280 	//		for (i = 0; i < 3; i++)
5281 	//		{
5282 	//			if (cameraMotion[i] > 0.0f)
5283 	//			{
5284 	//				cameraMotion[i] = 2.0;
5285 	//			}
5286 	//			else if (cameraMotion[i] < 0.0f)
5287 	//			{
5288 	//				cameraMotion[i] = -2.0;
5289 	//			}
5290 	//		}
5291 	//		break;
5292 	//	default:
5293 	//		return 1;
5294 	//		break;
5295 	//	}
5296 	//	modelViewer->setCameraMotion(cameraMotion);
5297 	//	forceRedraw(2);
5298 	//	return 0;
5299 	//}
5300 	//return 1;
5301 }
5302 
processKeyUp(int keyCode,LPARAM)5303 LRESULT ModelWindow::processKeyUp(int keyCode, LPARAM /*keyData*/)
5304 {
5305 	if (inputHandler->keyUp(getCurrentKeyModifiers(),
5306 		convertKeyCode(keyCode)))
5307 	{
5308 		return 0;
5309 	}
5310 	return 1;
5311 	//if (modelViewer && viewMode == LDVViewFlythrough)
5312 	//{
5313 	//	TCVector cameraMotion = modelViewer->getCameraMotion();
5314 	//	int i;
5315 
5316 	//	switch (keyCode)
5317 	//	{
5318 	//	case 'W':
5319 	//		cameraMotion[2] = 0.0f;
5320 	//		break;
5321 	//	case 'S':
5322 	//		cameraMotion[2] = 0.0f;
5323 	//		break;
5324 	//	case 'A':
5325 	//		cameraMotion[0] = 0.0f;
5326 	//		break;
5327 	//	case 'D':
5328 	//		cameraMotion[0] = 0.0f;
5329 	//		break;
5330 	//	case 'R':
5331 	//		cameraMotion[1] = 0.0f;
5332 	//		break;
5333 	//	case 'F':
5334 	//		cameraMotion[1] = 0.0f;
5335 	//		break;
5336 	//	case 'E':
5337 	//		modelViewer->setCameraZRotate(0.0f);
5338 	//		break;
5339 	//	case 'Q':
5340 	//		modelViewer->setCameraZRotate(0.0f);
5341 	//		break;
5342 	//	case VK_UP:
5343 	//		modelViewer->setCameraYRotate(0.0f);
5344 	//		break;
5345 	//	case VK_DOWN:
5346 	//		modelViewer->setCameraYRotate(0.0f);
5347 	//		break;
5348 	//	case VK_LEFT:
5349 	//		modelViewer->setCameraXRotate(0.0f);
5350 	//		break;
5351 	//	case VK_RIGHT:
5352 	//		modelViewer->setCameraXRotate(0.0f);
5353 	//		break;
5354 	//	case VK_SHIFT:
5355 	//		for (i = 0; i < 3; i++)
5356 	//		{
5357 	//			if (cameraMotion[i] > 0.0f)
5358 	//			{
5359 	//				cameraMotion[i] = 1.0;
5360 	//			}
5361 	//			else if (cameraMotion[i] < 0.0f)
5362 	//			{
5363 	//				cameraMotion[i] = -1.0;
5364 	//			}
5365 	//		}
5366 	//		break;
5367 	//	default:
5368 	//		return 1;
5369 	//		break;
5370 	//	}
5371 	//	modelViewer->setCameraMotion(cameraMotion);
5372 	//	forceRedraw(2);
5373 	//	return 0;
5374 	//}
5375 	//return 1;
5376 }
5377 
doMove(int newX,int newY)5378 LRESULT ModelWindow::doMove(int newX, int newY)
5379 {
5380 	return CUIOGLWindow::doMove(newX, newY);
5381 }
5382 
makeCurrent(void)5383 void ModelWindow::makeCurrent(void)
5384 {
5385 	if (initializedGL)
5386 	{
5387 		if (hCurrentDC && hCurrentGLRC)
5388 		{
5389 			wglMakeCurrent(hCurrentDC, hCurrentGLRC);
5390 		}
5391 		else
5392 		{
5393 			CUIOGLWindow::makeCurrent();
5394 		}
5395 	}
5396 }
5397 
performHotKey(int hotKeyIndex)5398 bool ModelWindow::performHotKey(int hotKeyIndex)
5399 {
5400 	if (prefs)
5401 	{
5402 		return prefs->performHotKey(hotKeyIndex);
5403 	}
5404 	else
5405 	{
5406 		return false;
5407 	}
5408 }
5409 
initFail(char *)5410 void ModelWindow::initFail(char * /*reason*/)
5411 {
5412 	MessageBox(hWindow, ls(_UC("OpenGlInitFailed")),
5413 		ls(_UC("FatalError")), MB_OK | MB_ICONERROR);
5414 	PostQuitMessage(-1);
5415 }
5416 
drawLight(GLenum,TCFloat,TCFloat,TCFloat)5417 void ModelWindow::drawLight(GLenum /*light*/, TCFloat /*x*/, TCFloat /*y*/,
5418 							TCFloat /*z*/)
5419 {
5420 	// Don't call super.
5421 }
5422 
drawLights(void)5423 void ModelWindow::drawLights(void)
5424 {
5425 	// Don't call super.
5426 }
5427 
orthoView(void)5428 void ModelWindow::orthoView(void)
5429 {
5430 	// Don't call super.
5431 }
5432 
setupLight(GLenum)5433 void ModelWindow::setupLight(GLenum /*light*/)
5434 {
5435 	// Don't call super.
5436 }
5437 
setSaveZoomToFit(bool value,bool commit)5438 void ModelWindow::setSaveZoomToFit(bool value, bool commit /*= false*/)
5439 {
5440 	saveZoomToFit = value;
5441 	if (commit)
5442 	{
5443 		TCUserDefaults::setLongForKey(value, SAVE_ZOOM_TO_FIT_KEY, false);
5444 	}
5445 }
5446 
boundingBoxToggled(void)5447 void ModelWindow::boundingBoxToggled(void)
5448 {
5449 	((LDViewWindow*)parentWindow)->boundingBoxToggled();
5450 }
5451