1 #include "LDHtmlInventory.h"
2 #include "LDPartsList.h"
3 #include "LDPartCount.h"
4 #include <LDLoader/LDLMainModel.h>
5 #include <TCFoundation/TCLocalStrings.h>
6 #include <TCFoundation/mystring.h>
7 #include "LDPreferences.h"
8 #include "LDrawModelViewer.h"
9 #include "LDViewPoint.h"
10 #include <string>
11 
12 #ifdef WIN32
13 #if defined(_MSC_VER) && _MSC_VER >= 1400 && defined(_DEBUG)
14 #define new DEBUG_CLIENTBLOCK
15 #endif // _DEBUG
16 #endif // WIN32
17 
18 const char *LDHtmlInventory::sm_style = "\
19 body\n\
20 {\n\
21 	font-family: tahoma, sans-serif;\n\
22 }\n\
23 \n\
24 div\n\
25 {\n\
26 	position: absolute;\n\
27 }\n\
28 \n\
29 table\n\
30 {\n\
31 	border-collapse: collapse;\n\
32 	border: 2px solid #000000;\n\
33 	background-color: #FFFFDD;\n\
34 	padding: 0px;\n\
35 }\n\
36 \n\
37 table.titleImage\n\
38 {\n\
39 	width: 100%;\n\
40 	border-bottom: 0px;\n\
41 }\n\
42 \n\
43 th.titleImage\n\
44 {\n\
45 	background-color: #EEEEEE;\n\
46 	color: #000000;\n\
47 	font-size: 150%;\n\
48 	border-bottom-style: none;\n\
49 	padding-bottom: 0px;\n\
50 }\n\
51 \n\
52 tr\n\
53 {\n\
54 	page-break-inside: avoid;\n\
55 }\n\
56 \n\
57 th\n\
58 {\n\
59 	background-color: #0080D0;\n\
60 	color: #FFFFFF;\n\
61 	border-bottom: 1px solid #000000;\n\
62 	border-right: 1px solid #00558A;\n\
63 	padding: 4px 8px;\n\
64 }\n\
65 \n\
66 th.title\n\
67 {\n\
68 	background-color: #EEEEEE;\n\
69 	color: #000000;\n\
70 	font-size: 150%;\n\
71 }\n\
72 \n\
73 td\n\
74 {\n\
75 	vertical-align: top;\n\
76 	background-color: #FFFFDD;\n\
77 	border-bottom: 1px solid #BFBFA5;\n\
78 	border-right: 1px solid #BFBFA5;\n\
79 	padding: 2px 8px;\n\
80 }\n\
81 \n\
82 td.quantity\n\
83 {\n\
84 	text-align: right;\n\
85 }\n\
86 \n\
87 td.colorNumber\n\
88 {\n\
89 	text-align: right;\n\
90 	border-right-style: none;\n\
91 }\n\
92 \n\
93 td.image\n\
94 {\n\
95 	border-right-style: none;\n\
96 }\n\
97 \n\
98 table.color\n\
99 {\n\
100 	border-style: none;\n\
101 	width: 100%;\n\
102 }\n\
103 \n\
104 table.color td\n\
105 {\n\
106 	border-style: none;\n\
107 	padding: 0px 8px 0px 0px;\n\
108 	width: 100%\n\
109 }\n\
110 \n\
111 table.color td.colorBox\n\
112 {\n\
113 	padding: 1px 0px;\n\
114 	width: auto\n\
115 }\n\
116 \n\
117 table.colorBox\n\
118 {\n\
119 	border: 1px solid black;\n\
120 	width: 2em;\n\
121 	height: 1em;\n\
122 	padding: 0px;\n\
123 }\n\
124 \n\
125 table.colorBox td\n\
126 {\n\
127 	padding: 0px;\n\
128 }\n\
129 \n\
130 table.credits\n\
131 {\n\
132 	border-collapse: collapse;\n\
133 	border-style: none;\n\
134 	background-color: transparent;\n\
135 	margin: 0px;\n\
136 	width: 100%;\n\
137 }\n\
138 \n\
139 td.credits\n\
140 {\n\
141 	background-color: #EEEEEE;\n\
142 	border-style: none;\n\
143 	padding: 0px;\n\
144 }\n\
145 \n\
146 table.credits td\n\
147 {\n\
148 	background-color: transparent;\n\
149 	color: #808080;\n\
150 	border-style: none;\n\
151 	font-size: 65%;\n\
152 }\n\
153 \n\
154 a\n\
155 {\n\
156 	text-decoration: none;\n\
157 	border-style: none;\n\
158 }\n\
159 \n\
160 a:link\n\
161 {\n\
162 	color: #000080;\n\
163 }\n\
164 \n\
165 a:visited\n\
166 {\n\
167 	color: #0000FF;\n\
168 }\n\
169 \n\
170 a:hover\n\
171 {\n\
172 	text-decoration: underline;\n\
173 	color: #000080;\n\
174 }\n\
175 \n\
176 img\n\
177 {\n\
178 	float: right;\n\
179 }\n\
180 \n\
181 th img\n\
182 {\n\
183 	float: none;\n\
184 }\n\
185 \n\
186 a img\n\
187 {\n\
188 	border-style: none;\n\
189 }\n\
190 \n\
191 :link:hover img\n\
192 {\n\
193 	background-color: #D0E8FF;\n\
194 }\n\
195 \n\
196 :visited:hover img\n\
197 {\n\
198 	background-color: #A0C0FF;\n\
199 }\n\
200 ";
201 
202 const char *LDHtmlInventory::sm_cssHeader = "\
203 /* CSS Style Sheet auto-generated by LDView */\n\
204 \n\
205 ";
206 
207 const char *LDHtmlInventory::sm_cssFilename = "LDViewPartsList.css";
208 
LDHtmlInventory(void)209 LDHtmlInventory::LDHtmlInventory(void) :
210 	m_prefs(new LDPreferences),
211 	m_viewPoint(NULL)
212 {
213 	int i;
214 
215 	m_prefs->loadInventorySettings();
216 	m_showModel = m_prefs->getInvShowModel();
217 	m_overwriteSnapshot = m_prefs->getInvOverwriteSnapshot();
218 	m_externalCss = m_prefs->getInvExternalCss();
219 	m_partImages = m_prefs->getInvPartImages();
220 	m_showFile = m_prefs->getInvShowFile();
221 	m_showTotal = m_prefs->getInvShowTotal();
222 	m_lastSavePath = m_prefs->getInvLastSavePath();
223 	const LongVector &columnOrder = m_prefs->getInvColumnOrder();
224 	for (i = 0; i < (int)columnOrder.size(); i++)
225 	{
226 		m_columnOrder.push_back((LDPartListColumn)columnOrder[i]);
227 	}
228 }
229 
~LDHtmlInventory(void)230 LDHtmlInventory::~LDHtmlInventory(void)
231 {
232 }
233 
dealloc(void)234 void LDHtmlInventory::dealloc(void)
235 {
236 	m_prefs->release();
237 	TCObject::dealloc();
238 }
239 
populateColumnMap(void)240 void LDHtmlInventory::populateColumnMap(void)
241 {
242 	int i;
243 
244 	m_columns = (int)m_columnOrder.size();
245 	m_columnMap.clear();
246 	for (i = 0; i < (int)m_columnOrder.size(); i++)
247 	{
248 		LDPartListColumn column = m_columnOrder[i];
249 
250 		m_columnMap[column] = true;
251 		switch (column)
252 		{
253 		case LDPLCPart:
254 			if (m_partImages)
255 			{
256 				m_columns++;
257 			}
258 			break;
259 		case LDPLCColor:
260 			m_columns++;
261 			break;
262 		default:
263 			// Get rid of gcc warning
264 			break;
265 		}
266 	}
267 }
268 
generateHtml(const char * filename,LDPartsList * partsList,const char * modelName)269 bool LDHtmlInventory::generateHtml(
270 	const char *filename,
271 	LDPartsList *partsList,
272 	const char *modelName)
273 {
274 	FILE *file = ucfopen(filename, "w");
275 	size_t nSlashSpot;
276 
277 	m_lastFilename = filename;
278 	m_lastSavePath = filename;
279 	populateColumnMap();
280 	nSlashSpot = m_lastSavePath.find_last_of("/\\");
281 	if (nSlashSpot < m_lastSavePath.size())
282 	{
283 		m_lastSavePath = m_lastSavePath.substr(0, nSlashSpot);
284 	}
285 	m_prefs->setInvLastSavePath(m_lastSavePath.c_str());
286 	m_prefs->commitInventorySettings();
287 	m_modelName = modelName;
288 	nSlashSpot = m_modelName.find_last_of("/\\");
289 	if (nSlashSpot < m_modelName.size())
290 	{
291 		m_modelName = m_modelName.substr(nSlashSpot + 1);
292 	}
293 	if (file)
294 	{
295 		const LDPartCountVector &partCounts = partsList->getPartCounts();
296 		int i, j;
297 
298 		writeHeader(file);
299 		writeTableHeader(file, partsList->getTotalParts());
300 		for (i = 0; i < (int)partCounts.size(); i++)
301 		{
302 			const LDPartCount &partCount = partCounts[i];
303 			const IntVector &colors = partCount.getColors();
304 			LDLModel *model = const_cast<LDLModel *>(partCount.getModel());
305 			LDLPalette *palette = model->getMainModel()->getPalette();
306 			//int partTotal = partCount.getTotalCount();
307 
308 			for (j = 0; j < (int)colors.size(); j++)
309 			{
310 				int colorNumber = colors[j];
311 				LDLColorInfo colorInfo = palette->getAnyColorInfo(colorNumber);
312 
313 				writePartRow(file, partCount, palette, colorInfo, colorNumber);
314 			}
315 		}
316 		writeTableFooter(file);
317 		writeFooter(file);
318 		fclose(file);
319 		return true;
320 	}
321 	else
322 	{
323 		return false;
324 	}
325 }
326 
safeOpenCssFile(const std::string & cssFilename,bool & match)327 FILE *LDHtmlInventory::safeOpenCssFile(const std::string &cssFilename,
328 									   bool &match)
329 {
330 	FILE *cssFile = ucfopen(cssFilename.c_str(), "r");
331 
332 	match = false;
333 	if (cssFile)
334 	{
335 		size_t headerSize = strlen(sm_cssHeader);
336 		size_t contentSize = strlen(sm_style);
337 
338 		std::string fileHeader;
339 		std::string fileContents;
340 
341 		fileHeader.resize(headerSize);
342 		fileContents.resize(contentSize);
343 		if (fread(&fileHeader[0], headerSize, 1, cssFile) == 1 &&
344 			fread(&fileContents[0], contentSize, 1, cssFile) == 1 &&
345 			fileHeader == sm_cssHeader &&
346 			fileContents == sm_style)
347 		{
348 			TCByte test;
349 
350 			if (fread(&test, 1, 1, cssFile) == 0)
351 			{
352 				match = true;
353 			}
354 		}
355 		fclose(cssFile);
356 		return NULL;
357 	}
358 	return ucfopen(cssFilename.c_str(), "w");
359 }
360 
writeExternalCss(void)361 bool LDHtmlInventory::writeExternalCss(void)
362 {
363 	if (m_externalCss)
364 	{
365 		std::string cssFilename = m_lastSavePath + "/" + sm_cssFilename;
366 		bool match;
367 		FILE *cssFile = safeOpenCssFile(cssFilename, match);
368 
369 		if (cssFile)
370 		{
371 			fprintf(cssFile, "%s", sm_cssHeader);
372 			fprintf(cssFile, "%s", sm_style);
373 			fclose(cssFile);
374 		}
375 		if (cssFile || match)
376 		{
377 			return true;
378 		}
379 	}
380 	return false;
381 }
382 
writeHeader(FILE * file)383 void LDHtmlInventory::writeHeader(FILE *file)
384 {
385 	//unsigned char utf8Signature[3] = { 0xEF, 0xBB, 0xBF };
386 	//fwrite(utf8Signature, 3, 1, file);
387 	fprintf(file, "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" "
388 		"\"http://www.w3.org/TR/html4/strict.dtd\">\n");
389 	fprintf(file, "<html>\n");
390 	fprintf(file, "<head>\n");
391 	fprintf(file, "<meta http-equiv=\"Content-Type\" "
392 		"content=\"text/html; charset=UTF-8\">\n");
393 	char title[1024];
394 	sprintf(title, lsUtf8("PLTitle"), m_modelName.c_str());
395 	fprintf(file, "<title>%s</title>\n", title);
396 	if (writeExternalCss())
397 	{
398 		fprintf(file, "<link href=\"%s\" title=\"%s\" "
399 			"rel=\"stylesheet\" type=\"text/css\">\n", sm_cssFilename,
400 			lsUtf8("PLStyleSheetTitle"));
401 	}
402 	else
403 	{
404 		fprintf(file, "<style type=\"text/css\" title=\"%s\"><!--\n",
405 			lsUtf8("PLStyleSheetTitle"));
406 		fprintf(file, "%s", sm_style);
407 		fprintf(file, "--></style>\n");
408 	}
409 	fprintf(file, "</head>\n");
410 	fprintf(file, "<body>\n");
411 	fprintf(file, "<div>\n");
412 }
413 
writeFooter(FILE * file)414 void LDHtmlInventory::writeFooter(FILE *file)
415 {
416 	fprintf(file, "</div>\n");
417 	fprintf(file, "</body>\n");
418 	fprintf(file, "</html>\n");
419 }
420 
writePartHeaderCell(FILE * file)421 void LDHtmlInventory::writePartHeaderCell(FILE *file)
422 {
423 	int colSpan = 2;
424 
425 	if (!m_partImages)
426 	{
427 		colSpan = 1;
428 	}
429 	writeHeaderCell(file, LDPLCPart, colSpan);
430 }
431 
writeHeaderCell(FILE * file,LDPartListColumn column,int colSpan)432 void LDHtmlInventory::writeHeaderCell(
433 	FILE *file,
434 	LDPartListColumn column,
435 	int colSpan)
436 {
437 	std::string utf8ColumnName;
438 	ucstringtoutf8(utf8ColumnName, getColumnNameUC(column));
439 	if (colSpan == 1)
440 	{
441 		fprintf(file, "			<th>%s</th>\n", utf8ColumnName.c_str());
442 	}
443 	else
444 	{
445 		fprintf(file, "			<th colspan=\"%d\">%s</th>\n", colSpan,
446 			utf8ColumnName.c_str());
447 	}
448 }
449 
writeHeaderCell(FILE * file,LDPartListColumn column)450 void LDHtmlInventory::writeHeaderCell(FILE *file, LDPartListColumn column)
451 {
452 	switch (column)
453 	{
454 	case LDPLCPart:
455 		writePartHeaderCell(file);
456 		break;
457 	case LDPLCDescription:
458 		writeHeaderCell(file, LDPLCDescription, 1);
459 		break;
460 	case LDPLCColor:
461 		writeHeaderCell(file, LDPLCColor, 2);
462 		break;
463 	case LDPLCQuantity:
464 		writeHeaderCell(file, LDPLCQuantity, 1);
465 		break;
466 	}
467 }
468 
writePartCell(FILE * file,const LDPartCount & partCount,LDLPalette * palette,const LDLColorInfo & colorInfo,int colorNumber)469 void LDHtmlInventory::writePartCell(
470 	FILE *file,
471 	const LDPartCount &partCount,
472 	LDLPalette *palette,
473 	const LDLColorInfo &colorInfo,
474 	int colorNumber)
475 {
476 	int peeronColorNumber = colorNumber;
477 	std::string imgStyle;
478 	std::string partName = partCount.getFilename();
479 	size_t nDotSpot = partName.find('.');
480 	int r, g, b, a;
481 
482 	palette->getRGBA(colorInfo, r, g, b, a);
483 	if (nDotSpot < partName.size())
484 	{
485 		partName = partName.substr(0, nDotSpot);
486 	}
487 	if (peeronColorNumber >= 512)
488 	{
489 		char bgColor[10];
490 
491 		sprintf(bgColor, "%02X%02X%02X", r, g, b);
492 		peeronColorNumber = 7;
493 		imgStyle = (std::string)"style = \"padding: 4px; "
494 			"background-color: #" + bgColor + "\" ";
495 	}
496 	if (m_partImages)
497 	{
498 		std::string className;
499 		bool official = partCount.getModel()->isOfficial();
500 
501 		//if (official)
502 		{
503 			className = " class=\"image\"";
504 		}
505 		fprintf(file, "			<td%s>"
506 			"<a href=\"http://peeron.com/inv/parts/%s\">",
507 			className.c_str(), partName.c_str());
508 		if (official)
509 		{
510 			fprintf(file, "<img %salt=\"%s\" title=\"%s\" "
511 				"src=\"http://media.peeron.com/ldraw/images/%d/100/%s.png\">",
512 				imgStyle.c_str(), lsUtf8("PLViewOnPeeron"),
513 				lsUtf8("PLViewOnPeeron"), peeronColorNumber, partName.c_str());
514 		}
515 		else
516 		{
517 			fprintf(file, "%s", lsUtf8("PLViewOnPeeron"));
518 		}
519 		fprintf(file, "</a></td>\n");
520 	}
521 	fprintf(file, "			<td>%s</td>\n", partName.c_str());
522 }
523 
writeDescriptionCell(FILE * file,const LDPartCount & partCount)524 void LDHtmlInventory::writeDescriptionCell(
525 	FILE *file,
526 	const LDPartCount &partCount)
527 {
528 	if (m_columnMap[LDPLCDescription])
529 	{
530 		const char *description = partCount.getModel()->getDescription();
531 		if (description)
532 		{
533 			fprintf(file, "			<td>%s</td>\n", description);
534 		}
535 		else
536 		{
537 			fprintf(file, "			<td>&lt;%s&gt;</td>\n",
538 				lsUtf8("PLNoDescription"));
539 		}
540 	}
541 }
542 
writeColorCell(FILE * file,LDLPalette * palette,const LDLColorInfo & colorInfo,int colorNumber)543 void LDHtmlInventory::writeColorCell(
544 	FILE *file,
545 	LDLPalette *palette,
546 	const LDLColorInfo &colorInfo,
547 	int colorNumber)
548 {
549 	int r, g, b, a;
550 
551 	palette->getRGBA(colorInfo, r, g, b, a);
552 	if (strlen(colorInfo.name))
553 	{
554 		fprintf(file, "			<td class=\"colorNumber\">%d:</td>\n",
555 			colorNumber);
556 	}
557 	else
558 	{
559 		if (colorNumber < 512)
560 		{
561 			fprintf(file, "			<td class=\"colorNumber\">%d"
562 				"</td>\n", colorNumber);
563 		}
564 		else
565 		{
566 			fprintf(file, "			<td class=\"colorNumber\">#%X"
567 				"</td>\n", colorNumber);
568 		}
569 	}
570 	fprintf(file, "			<td>\n");
571 	fprintf(file, "				<table class=\"color\">\n");
572 	fprintf(file, "					<tr>\n");
573 	fprintf(file, "						<td rowspan=\"2\">"
574 		"%s<td>\n", colorInfo.name);
575 	fprintf(file, "						<td class=\"colorBox\"><table "
576 		"class=\"colorBox\"><tr><td style=\"background-color: #%02X%02X%02X\">"
577 		"&nbsp;</td></tr></table></td>\n", r, g, b);
578 	fprintf(file, "					</tr>\n");
579 	fprintf(file, "				</table>\n");
580 	fprintf(file, "			</td>\n");
581 }
582 
writeQuantityCell(FILE * file,const LDPartCount & partCount,int colorNumber)583 void LDHtmlInventory::writeQuantityCell(
584 	FILE *file,
585 	const LDPartCount &partCount,
586 	int colorNumber)
587 {
588 	fprintf(file, "			<td class=\"quantity\">%d</td>\n",
589 		partCount.getColorCount(colorNumber));
590 }
591 
writeCell(FILE * file,LDPartListColumn column,const LDPartCount & partCount,LDLPalette * palette,const LDLColorInfo & colorInfo,int colorNumber)592 void LDHtmlInventory::writeCell(
593 	FILE *file, LDPartListColumn column,
594 	const LDPartCount &partCount,
595 	LDLPalette *palette,
596 	const LDLColorInfo &colorInfo,
597 	int colorNumber)
598 {
599 	switch (column)
600 	{
601 	case LDPLCPart:
602 		writePartCell(file, partCount, palette, colorInfo, colorNumber);
603 		break;
604 	case LDPLCDescription:
605 		writeDescriptionCell(file, partCount);
606 		break;
607 	case LDPLCColor:
608 		writeColorCell(file, palette, colorInfo, colorNumber);
609 		break;
610 	case LDPLCQuantity:
611 		writeQuantityCell(file, partCount, colorNumber);
612 		break;
613 	}
614 }
615 
getSnapshotPath(void) const616 const char *LDHtmlInventory::getSnapshotPath(void) const
617 {
618 	m_snapshotPath = m_lastSavePath + "/" + getSnapshotFilename();
619 	return m_snapshotPath.c_str();
620 }
621 
getSnapshotFilename(void) const622 std::string LDHtmlInventory::getSnapshotFilename(void) const
623 {
624 	size_t nSpot = m_lastFilename.find_last_of('.');
625 	std::string filename;
626 
627 	if (nSpot < m_lastFilename.size())
628 	{
629 		filename = m_lastFilename.substr(0, nSpot);
630 	}
631 	else
632 	{
633 		filename = m_lastFilename;
634 	}
635 	filename += ".png";
636 	nSpot = filename.find_last_of("/\\");
637 	if (nSpot < filename.size())
638 	{
639 		filename = filename.substr(nSpot + 1);
640 	}
641 	return filename;
642 }
643 
writeTableHeader(FILE * file,int totalParts)644 void LDHtmlInventory::writeTableHeader(FILE *file, int totalParts)
645 {
646 	size_t i;
647 
648 	fprintf(file, "<table class=\"titleImage\">\n");
649 	fprintf(file, "	<tbody>\n");
650 	if (m_showModel)
651 	{
652 		fprintf(file, "		<tr>\n");
653 		fprintf(file, "			<th class=\"titleImage\" "
654 			"colspan=\"%d\">\n", m_columns);
655 		fprintf(file, "				<img alt=\"&lt;%s&gt;\" "
656 			"title=\"&lt;%s&gt;\" src=\"%s\">\n",
657 			m_modelName.c_str(), m_modelName.c_str(),
658 			getSnapshotFilename().c_str());
659 		fprintf(file, "			</th>\n");
660 		fprintf(file, "		</tr>\n");
661 	}
662 	fprintf(file, "		<tr>\n");
663 	fprintf(file, "			<th class=\"title\" colspan=\"%d\">\n", m_columns);
664 	char title[1024];
665 	sprintf(title, lsUtf8("PLTitle"), m_modelName.c_str());
666 	fprintf(file, "				%s", title);
667 	if (m_showTotal)
668 	{
669 		fprintf(file, lsUtf8("PLTotalParts"), totalParts);
670 	}
671 	fprintf(file, "\n");
672 	fprintf(file, "			</th>\n");
673 	fprintf(file, "		</tr>\n");
674 	fprintf(file, "	</tbody>\n");
675 	fprintf(file, "</table>\n");
676 	fprintf(file, "<table>\n");
677 	fprintf(file, "	<thead>\n");
678 	fprintf(file, "		<tr>\n");
679 	for (i = 0; i < m_columnOrder.size(); i++)
680 	{
681 		writeHeaderCell(file, m_columnOrder[i]);
682 	}
683 	fprintf(file, "		</tr>\n");
684 	fprintf(file, "	</thead>\n");
685 	fprintf(file, "	<tbody>\n");
686 }
687 
writeTableFooter(FILE * file)688 void LDHtmlInventory::writeTableFooter(FILE *file)
689 {
690 	const char *ldviewCreditAlign = "left";
691 
692 	//fprintf(file, "	<tfoot>\n");
693 	fprintf(file, "		<tr>\n");
694 	fprintf(file, "			<td colspan=\"%d\" class=\"credits\">\n",
695 			m_columns);
696 	fprintf(file, "				<table class=\"credits\">\n");
697 	fprintf(file, "					<tbody>\n");
698 	fprintf(file, "						<tr>\n");
699 	if (!m_partImages)
700 	{
701 		ldviewCreditAlign = "center";
702 	}
703 	fprintf(file, "							<td align=\"%s\">\n",
704 			ldviewCreditAlign);
705 	fprintf(file, "								%s\n",
706 			lsUtf8("PLGeneratedBy"));
707 	fprintf(file, "							</td>\n");
708 	if (m_partImages)
709 	{
710 		fprintf(file, "							<td align=\"right\">\n");
711 		fprintf(file, "								%s\n",
712 				lsUtf8("PLProvidedBy"));
713 		fprintf(file, "							</td>\n");
714 	}
715 	fprintf(file, "						</tr>\n");
716 	fprintf(file, "					</tbody>\n");
717 	fprintf(file, "				</table>\n");
718 	fprintf(file, "			</td>\n");
719 	fprintf(file, "		</tr>\n");
720 	//fprintf(file, "	</tfoot>\n");
721 	fprintf(file, "	</tbody>\n");
722 	fprintf(file, "</table>\n");
723 }
724 
writePartRow(FILE * file,const LDPartCount & partCount,LDLPalette * palette,const LDLColorInfo & colorInfo,int colorNumber)725 void LDHtmlInventory::writePartRow(
726 	FILE *file,
727 	const LDPartCount &partCount,
728 	LDLPalette *palette,
729 	const LDLColorInfo &colorInfo,
730 	int colorNumber)
731 {
732 	size_t i;
733 
734 	fprintf(file, "		<tr>\n");
735 	for (i = 0; i < m_columnOrder.size(); i++)
736 	{
737 		writeCell(file, m_columnOrder[i], partCount, palette, colorInfo,
738 			colorNumber);
739 	}
740 	fprintf(file, "		</tr>\n");
741 }
742 
setShowModelFlag(bool value)743 void LDHtmlInventory::setShowModelFlag(bool value)
744 {
745 	m_showModel = value;
746 	m_prefs->setInvShowModel(value);
747 }
748 
setOverwriteSnapshotFlag(bool value)749 void LDHtmlInventory::setOverwriteSnapshotFlag(bool value)
750 {
751 	m_overwriteSnapshot = value;
752 	m_prefs->setInvOverwriteSnapshot(value);
753 }
754 
setExternalCssFlag(bool value)755 void LDHtmlInventory::setExternalCssFlag(bool value)
756 {
757 	m_externalCss = value;
758 	m_prefs->setInvExternalCss(value);
759 }
760 
setPartImagesFlag(bool value)761 void LDHtmlInventory::setPartImagesFlag(bool value)
762 {
763 	m_partImages = value;
764 	m_prefs->setInvPartImages(value);
765 }
766 
setShowFileFlag(bool value)767 void LDHtmlInventory::setShowFileFlag(bool value)
768 {
769 	// Note: this class doesn't actually use the flag; it just keeps track of
770 	// the value and makes sure it gets saved and restored in the user defaults.
771 	m_showFile = value;
772 	m_prefs->setInvShowFile(value);
773 }
774 
setShowTotalFlag(bool value)775 void LDHtmlInventory::setShowTotalFlag(bool value)
776 {
777 	m_showTotal = value;
778 	m_prefs->setInvShowTotal(value);
779 }
780 
setColumnOrder(const LDPartListColumnVector & value)781 void LDHtmlInventory::setColumnOrder(const LDPartListColumnVector &value)
782 {
783 	LongVector columnOrder;
784 	int i;
785 
786 	m_columnOrder = value;
787 	for (i = 0; i < (int)value.size(); i++)
788 	{
789 		columnOrder.push_back(value[i]);
790 	}
791 	m_prefs->setInvColumnOrder(columnOrder);
792 }
793 
isColumnEnabled(LDPartListColumn column)794 bool LDHtmlInventory::isColumnEnabled(LDPartListColumn column)
795 {
796 	populateColumnMap();
797 	return m_columnMap[column];
798 }
799 
getColumnNameUC(LDPartListColumn column)800 CUCSTR LDHtmlInventory::getColumnNameUC(LDPartListColumn column)
801 {
802 	switch (column)
803 	{
804 	case LDPLCPart:
805 		return ls(_UC("PLPartColName"));
806 		break;
807 	case LDPLCDescription:
808 		return ls(_UC("PLDescriptionColName"));
809 		break;
810 	case LDPLCColor:
811 		return ls(_UC("PLColorColName"));
812 		break;
813 	case LDPLCQuantity:
814 		return ls(_UC("PLQuantityColName"));
815 		break;
816 	}
817 	return _UC("<Unknown Column Name>");
818 }
819 
getColumnName(LDPartListColumn column)820 const char *LDHtmlInventory::getColumnName(LDPartListColumn column)
821 {
822 	switch (column)
823 	{
824 	case LDPLCPart:
825 		return ls("PLPartColName");
826 		break;
827 	case LDPLCDescription:
828 		return ls("PLDescriptionColName");
829 		break;
830 	case LDPLCColor:
831 		return ls("PLColorColName");
832 		break;
833 	case LDPLCQuantity:
834 		return ls("PLQuantityColName");
835 		break;
836 	}
837 	return "<Unknown Column Name>";
838 }
839 
prepForSnapshot(LDrawModelViewer * modelViewer)840 void LDHtmlInventory::prepForSnapshot(LDrawModelViewer *modelViewer)
841 {
842 	TCObject::release(m_viewPoint);
843 	m_viewPoint = modelViewer->saveViewPoint();
844 	modelViewer->resetView();
845 	modelViewer->setRotationSpeed(0);
846 	modelViewer->setXYPan(0, 0);
847 	modelViewer->setBackgroundRGB(0xEE, 0xEE, 0xEE);
848 	modelViewer->setStereoMode(LDVStereoNone);
849 }
850 
restoreAfterSnapshot(LDrawModelViewer * modelViewer)851 void LDHtmlInventory::restoreAfterSnapshot(LDrawModelViewer *modelViewer)
852 {
853 	if (m_viewPoint)
854 	{
855 		modelViewer->restoreViewPoint(m_viewPoint);
856 		m_viewPoint->release();
857 	}
858 }
859 
isSnapshotNeeded(void) const860 bool LDHtmlInventory::isSnapshotNeeded(void) const
861 {
862 	if (m_showModel)
863 	{
864 		if (m_overwriteSnapshot)
865 		{
866 			return true;
867 		}
868 		else
869 		{
870 			const char *snapshotPath = getSnapshotPath();
871 			FILE *pFile = ucfopen(snapshotPath, "rb");
872 
873 			if (pFile)
874 			{
875 				fclose(pFile);
876 			}
877 			else
878 			{
879 				return true;
880 			}
881 		}
882 	}
883 	return false;
884 }
885 
defaultFilename(const char * modelFilename)886 std::string LDHtmlInventory::defaultFilename(const char *modelFilename)
887 {
888 	char *filePart = filenameFromPath(modelFilename);
889 	char *dirPart = directoryFromPath(modelFilename);
890 	std::string htmlFilename;
891 
892 	if (dirPart)
893 	{
894 		htmlFilename = dirPart;
895 		delete[] dirPart;
896 		htmlFilename += '/';
897 	}
898 	char *findSpot = strrchr(filePart, '.');
899 	if (findSpot)
900 	{
901 		findSpot[0] = 0;
902 	}
903 	htmlFilename += filePart;
904 	delete[] filePart;
905 	htmlFilename += ".html";
906 	filePart = cleanedUpPath(htmlFilename.c_str());
907 	htmlFilename = filePart;
908 	delete[] filePart;
909 	return htmlFilename;
910 }
911