Added primitive scanning, replaced parts list in subfile add dialog with it

Sat, 15 Jun 2013 04:20:44 +0300

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Sat, 15 Jun 2013 04:20:44 +0300
changeset 290
be0c367e7420
parent 289
d7bf5c11d299
child 291
c8547f780861

Added primitive scanning, replaced parts list in subfile add dialog with it

changelog.txt file | annotate | diff | comparison | revisions
src/addObjectDialog.cpp file | annotate | diff | comparison | revisions
src/addObjectDialog.h file | annotate | diff | comparison | revisions
src/config.cpp file | annotate | diff | comparison | revisions
src/file.cpp file | annotate | diff | comparison | revisions
src/file.h file | annotate | diff | comparison | revisions
src/gui.cpp file | annotate | diff | comparison | revisions
src/gui.h file | annotate | diff | comparison | revisions
src/gui_actions.cpp file | annotate | diff | comparison | revisions
src/main.cpp file | annotate | diff | comparison | revisions
src/types.h file | annotate | diff | comparison | revisions
--- a/changelog.txt	Sat Jun 15 01:29:46 2013 +0300
+++ b/changelog.txt	Sat Jun 15 04:20:44 2013 +0300
@@ -5,6 +5,8 @@
 - Completely rewrote history (undo/redo) code, making it a LOT stabler in the process.
 - Added ability to snap to pre-existing vertices while drawing.
 - When drawing, drawn vertices now display coordinate labels.
+- Replaced parts list in subfile item list with a primitive listing. Listing is generated either if
+	it's not cached (prims.cfg in configuration directory) or on user demand.
 - Added an export to file action, moved it + insert from to File menu
 - Parts are now zoomed to fit properly, making the initial view of the part clearer.
 - Replace coords: allow replacing all coords regardless of original value, plus relative moving (offset)
--- a/src/addObjectDialog.cpp	Sat Jun 15 01:29:46 2013 +0300
+++ b/src/addObjectDialog.cpp	Sat Jun 15 04:20:44 2013 +0300
@@ -34,15 +34,14 @@
 #include "widgets.h"
 #include "misc.h"
 
-// =============================================================================
 class SubfileListItem : public QTreeWidgetItem {
+	PROPERTY (PrimitiveInfo*, primInfo, setPrimInfo)
+	
 public:
-	SubfileListItem (QTreeWidgetItem* parent, int subfileID) :
-		QTreeWidgetItem (parent), subfileID (subfileID) {}
-	SubfileListItem (QTreeWidget* parent, int subfileID) :
-		QTreeWidgetItem (parent), subfileID (subfileID) {}
-	
-	int subfileID;
+	SubfileListItem (QTreeWidgetItem* parent, PrimitiveInfo* info) :
+		QTreeWidgetItem (parent), m_primInfo (info) {}
+	SubfileListItem (QTreeWidget* parent, PrimitiveInfo* info) :
+		QTreeWidgetItem (parent), m_primInfo (info) {}
 };
 
 // =============================================================================
@@ -92,60 +91,32 @@
 		break;
 	
 	case LDObject::Subfile:
-		coordCount = 3;
-		
-		enum {
-			Parts,
-			Subparts,
-			Primitives,
-			HiRes,
-		};
-		
-		tw_subfileList = new QTreeWidget ();
-/*
-		for (int i : vector<int> ({Parts, Subparts, Primitives, HiRes})) {
-			SubfileListItem* parentItem = new SubfileListItem (tw_subfileList, -1);
-			parentItem->setText (0, (i == Parts) ? "Parts" :
-				(i == Subparts) ? "Subparts" :
-				(i == Primitives) ? "Primitives" :
-				"Hi-Res");
+		{
+			coordCount = 3;
 			
-			ulong j = 0;
-			for (partListEntry& part : g_PartList) {
-				QList<QTreeWidgetItem*> subfileItems;
-				
-				str fileName = part.name;
-				const bool isSubpart = fileName.mid (0, 2) == "s\\";
-				const bool isPrimitive = part.title.mid (0, 9) == "Primitive";
-				const bool isHiRes = fileName.mid (0, 3) == "48\\";
-				
-				if ((i == Subparts && isSubpart) ||
-					(i == Primitives && isPrimitive) ||
-					(i == HiRes && isHiRes) ||
-					(i == Parts && !isSubpart && !isPrimitive && !isHiRes))
-				{
-					SubfileListItem* item = new SubfileListItem (parentItem, j);
-					item->setText (0, fmt ("%1 - %2", part.name, part.title));
-					subfileItems.append (item);
-				}
-				
-				j++;
+			tw_subfileList = new QTreeWidget ();
+			SubfileListItem* parentItem = new SubfileListItem (tw_subfileList, null);
+			parentItem->setText (0, "Primitives");
+			QList<QTreeWidgetItem*> subfileItems;
+			
+			for (PrimitiveInfo& info : g_Primitives) {
+				SubfileListItem* item = new SubfileListItem (parentItem, &info);
+				item->setText (0, fmt ("%1 - %2", info.name, info.title));
+				subfileItems << item;
 			}
 			
 			tw_subfileList->addTopLevelItem (parentItem);
+			connect (tw_subfileList, SIGNAL (itemSelectionChanged ()), this, SLOT (slot_subfileTypeChanged ()));
+			lb_subfileName = new QLabel ("File:");
+			le_subfileName = new QLineEdit;
+			le_subfileName->setFocus ();
+			
+			if (obj) {
+				LDSubfile* ref = static_cast<LDSubfile*> (obj);
+				le_subfileName->setText (ref->fileInfo ()->name ());
+			}
+			break;
 		}
-*/
-		
-		connect (tw_subfileList, SIGNAL (itemSelectionChanged ()), this, SLOT (slot_subfileTypeChanged ()));
-		lb_subfileName = new QLabel ("File:");
-		le_subfileName = new QLineEdit;
-		le_subfileName->setFocus ();
-		
-		if (obj) {
-			LDSubfile* ref = static_cast<LDSubfile*> (obj);
-			le_subfileName->setText (ref->fileInfo ()->name ());
-		}
-		break;
 	
 	case LDObject::Radial:
 		coordCount = 3;
@@ -311,7 +282,12 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 str AddObjectDialog::currentSubfileName () {
-	return "";
+	SubfileListItem* item = static_cast<SubfileListItem*> (tw_subfileList->currentItem ());
+	
+	if (item->primInfo () == null)
+		return ""; // selected a heading
+	
+	return item->primInfo ()->name;
 }
 
 // =============================================================================
--- a/src/addObjectDialog.h	Sat Jun 15 01:29:46 2013 +0300
+++ b/src/addObjectDialog.h	Sat Jun 15 04:20:44 2013 +0300
@@ -22,6 +22,7 @@
 #include <QDialog>
 #include "ldtypes.h"
 
+class QTreeWidgetItem;
 class QLineEdit;
 class RadioBox;
 class QCheckBox;
--- a/src/config.cpp	Sat Jun 15 01:29:46 2013 +0300
+++ b/src/config.cpp	Sat Jun 15 04:20:44 2013 +0300
@@ -69,8 +69,6 @@
 		str entry = line.left (equals);
 		str valstring = line.right (line.length () - equals - 1);
 		
-		print ("config: `%1` -> %2 == %3 (%4)\n", line, entry, valstring, equals);
-		
 		// Find the config entry for this.
 		config* cfg = null;
 		for (config* i : g_configPointers) {
--- a/src/file.cpp	Sat Jun 15 01:29:46 2013 +0300
+++ b/src/file.cpp	Sat Jun 15 04:20:44 2013 +0300
@@ -18,6 +18,7 @@
 
 #include <QMessageBox>
 #include <QFileDialog>
+#include <qprogressbar.h>
 #include <QDir>
 #include <qthread.h>
 
@@ -37,6 +38,9 @@
 cfg (str, io_recentfiles, "");
 
 static bool g_loadingMainFile = false;
+PrimitiveLister* g_activePrimLister = null;
+bool g_primListerMutex = false;
+vector<PrimitiveInfo> g_Primitives;
 
 // =============================================================================
 namespace LDPaths {
@@ -925,4 +929,108 @@
 	
 	g_loadedFiles.clear ();
 	g_loadedFiles << filesUsed;
+}
+
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
+void recursiveGetFilenames (QDir dir, vector<str>& fnames) {
+	QFileInfoList flist = dir.entryInfoList ();
+	for (const QFileInfo& info : flist) {
+		if (info.fileName () == "." || info.fileName () == "..")
+			continue; // skip . and ..
+		
+		if (info.isDir ())
+			recursiveGetFilenames (QDir (info.absoluteFilePath ()), fnames);
+		else
+			fnames << info.absoluteFilePath ();
+	}
+}
+
+void PrimitiveLister::work () {
+	g_activePrimLister = this;
+	m_prims.clear ();
+	
+	QDir dir (LDPaths::prims ());
+	assert (dir.exists ());
+	
+	ulong baselen = dir.absolutePath ().length ();
+	
+	vector<str> fnames;
+	recursiveGetFilenames (dir, fnames);
+	emit starting (fnames.size ());
+	
+	ulong i = 0;
+	for (str fname : fnames) {
+		File f (fname, File::Read);
+		
+		PrimitiveInfo info;
+		info.name = fname.mid (baselen + 1); // make full path relative
+		info.name.replace ('/', '\\'); // use DOS backslashes, they're expected
+		
+		if (!f.readLine (info.title))
+			info.title = "";
+		
+		info.title = info.title.simplified ();
+		
+		if (info.title[0] == '0') {
+			info.title.remove (0, 1); // remove 0
+			info.title = info.title.simplified ();
+		}
+		
+		m_prims << info;
+		emit update (++i);
+	}
+	
+	// Save to a config file
+	File conf (config::dirpath () + "prims.cfg", File::Write);
+	for (PrimitiveInfo& info : m_prims)
+		fprint (conf, "%1 %2\n", info.name, info.title);
+	
+	conf.close ();
+	
+	g_primListerMutex = true;
+	g_Primitives = m_prims;
+	g_primListerMutex = false;
+	
+	g_activePrimLister = null;
+	emit workDone ();
+}
+
+void PrimitiveLister::start () {
+	if (g_activePrimLister)
+		return;
+	
+	PrimitiveLister* lister = new PrimitiveLister;
+	QThread* listerThread = new QThread;
+	lister->moveToThread (listerThread);
+	connect (lister, SIGNAL (starting (ulong)), g_win, SLOT (primitiveLoaderStart (ulong)));
+	connect (lister, SIGNAL (update (ulong)), g_win, SLOT (primitiveLoaderUpdate (ulong)));
+	connect (lister, SIGNAL (workDone ()), g_win, SLOT (primitiveLoaderEnd ()));
+	connect (listerThread, SIGNAL (started ()), lister, SLOT (work ()));
+	connect (listerThread, SIGNAL (finished ()), lister, SLOT (deleteLater ()));
+	listerThread->start ();
+}
+
+void loadPrimitiveInfo () {
+	g_Primitives.clear ();
+	
+	// Try to load prims.cfg
+	File conf (config::dirpath () + "prims.cfg", File::Read);
+	if (!conf) {
+		// No prims.cfg, build it
+		PrimitiveLister::start ();
+		return;
+	}
+	
+	for (str line : conf) {
+		int space = line.indexOf (" ");
+		if (space == -1)
+			continue;
+		
+		PrimitiveInfo info;
+		info.name = line.left (space);
+		info.title = line.mid (space + 1);
+		g_Primitives << info;
+	}
 }
\ No newline at end of file
--- a/src/file.h	Sat Jun 15 01:29:46 2013 +0300
+++ b/src/file.h	Sat Jun 15 04:20:44 2013 +0300
@@ -26,6 +26,7 @@
 
 class History;
 class OpenProgressDialog;
+
 namespace LDPaths {
 	void initPaths ();
 	bool tryConfigure (str path);
@@ -138,6 +139,14 @@
 str basename (str path);
 str dirname (str path);
 
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
+// FileLoader
+// 
+// Loads the given file and parses it to LDObjects using parseLine. It's a
+// separate class so as to be able to do the work in a separate thread.
+// =============================================================================
 class FileLoader : public QObject {
 	Q_OBJECT
 	
@@ -158,4 +167,40 @@
 	void workDone ();
 };
 
+struct PrimitiveInfo {
+	str name, title;
+};
+
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
+// PrimitiveLister
+// 
+// Worker object that scans the primitives folder for primitives and
+// builds an index of them.
+// =============================================================================
+class PrimitiveLister : public QObject {
+	Q_OBJECT
+	
+public:
+	static void start ();
+	
+public slots:
+	void work ();
+	
+signals:
+	void starting (ulong num);
+	void workDone ();
+	void update (ulong i);
+	
+private:
+	vector<PrimitiveInfo> m_prims;
+};
+
+extern vector<PrimitiveInfo> g_Primitives;
+extern PrimitiveLister* g_activePrimLister;
+extern bool g_primListerMutex;
+
+void loadPrimitiveInfo ();
+
 #endif // FILE_H
\ No newline at end of file
--- a/src/gui.cpp	Sat Jun 15 01:29:46 2013 +0300
+++ b/src/gui.cpp	Sat Jun 15 04:20:44 2013 +0300
@@ -28,7 +28,10 @@
 #include <QComboBox>
 #include <QDialogButtonBox>
 #include <QToolBar>
+#include <QProgressBar>
+#include <QLabel>
 #include <QCoreApplication>
+#include <QTimer>
 
 #include "common.h"
 #include "gldraw.h"
@@ -88,6 +91,15 @@
 	slot_selectionChanged ();
 	
 	setStatusBar (new QStatusBar);
+	
+	m_primLoaderBar = new QProgressBar;
+	m_primLoaderWidget = new QWidget;
+	QHBoxLayout* primLoaderLayout = new QHBoxLayout (m_primLoaderWidget);
+	primLoaderLayout->addWidget (new QLabel ("Loading primitives:"));
+	primLoaderLayout->addWidget (m_primLoaderBar);
+	statusBar ()->addPermanentWidget (m_primLoaderWidget);
+	m_primLoaderWidget->hide ();
+	
 	setWindowIcon (getIcon ("ldforge"));
 	updateTitle ();
 	setMinimumSize (320, 200);
@@ -174,6 +186,7 @@
 	addMenuAction ("setLDrawPath");		// Set LDraw Path
 	menu->addSeparator ();					// -------
 	addMenuAction ("testpic");		// Set LDraw Path
+	addMenuAction ("reloadPrimitives");
 	menu->addSeparator ();					// -------
 	addMenuAction ("exit");				// Exit
 	
@@ -992,6 +1005,23 @@
 	AddObjectDialog::staticDialog (obj->getType (), obj);
 }
 
+void ForgeWindow::primitiveLoaderStart (ulong max) {
+	m_primLoaderWidget->show ();
+	m_primLoaderBar->setRange (0, max);
+	m_primLoaderBar->setValue (0);
+}
+
+void ForgeWindow::primitiveLoaderUpdate (ulong prog) {
+	m_primLoaderBar->setValue (prog);
+}
+
+void ForgeWindow::primitiveLoaderEnd () {
+	QTimer* hidetimer = new QTimer;
+	connect (hidetimer, SIGNAL (timeout ()), m_primLoaderWidget, SLOT (hide ()));
+	hidetimer->setSingleShot (true);
+	hidetimer->start (2000);
+}
+
 // ============================================================================
 void ObjectList::contextMenuEvent (QContextMenuEvent* ev) {
 	g_win->spawnContextMenu (ev->globalPos ());
@@ -1097,7 +1127,7 @@
 	}
 }
 
-// ========================================================================================================================================
+// =============================================================================
 QDialogButtonBox* makeButtonBox (QDialog& dlg) {
 	QDialogButtonBox* bbx_buttons = new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
 	QWidget::connect (bbx_buttons, SIGNAL (accepted ()), &dlg, SLOT (accept ()));
--- a/src/gui.h	Sat Jun 15 01:29:46 2013 +0300
+++ b/src/gui.h	Sat Jun 15 04:20:44 2013 +0300
@@ -25,7 +25,6 @@
 #include "config.h"
 #include "ldtypes.h"
 
-class QComboBox;
 class ForgeWindow;
 class color;
 class QSplitter;
@@ -33,6 +32,8 @@
 class QDialogButtonBox;
 class GLRenderer;
 class CheckBoxGroup;
+class QComboBox;
+class QProgressBar;
 
 // Stuff for dialogs
 #define IMPLEMENT_DIALOG_BUTTONS \
@@ -129,6 +130,11 @@
 	void setStatusBarText (str text);
 	void addActionMeta (actionmeta& meta);
 	
+public slots:
+	void primitiveLoaderStart (ulong max);
+	void primitiveLoaderUpdate (ulong prog);
+	void primitiveLoaderEnd ();
+	
 protected:
 	void closeEvent (QCloseEvent* ev);
 	void logVA (LogType eType, const char* fmtstr, va_list va);
@@ -143,6 +149,8 @@
 	QSplitter* m_splitter;
 	str m_msglogHTML;
 	QToolBar* m_colorToolBar;
+	QProgressBar* m_primLoaderBar;
+	QWidget* m_primLoaderWidget;
 	vector<QToolBar*> m_toolBars;
 	vector<LDObject*> m_sel;
 	vector<quickColor> m_colorMeta;
--- a/src/gui_actions.cpp	Sat Jun 15 01:29:46 2013 +0300
+++ b/src/gui_actions.cpp	Sat Jun 15 04:20:44 2013 +0300
@@ -494,4 +494,8 @@
 	
 	delete[] imgdata;
 	rend->deleteLater ();
+}
+
+MAKE_ACTION (reloadPrimitives, "Scan Primitives", "", "", (0)) {
+	PrimitiveLister::start ();
 }
\ No newline at end of file
--- a/src/main.cpp	Sat Jun 15 01:29:46 2013 +0300
+++ b/src/main.cpp	Sat Jun 15 04:20:44 2013 +0300
@@ -79,6 +79,8 @@
 	ForgeWindow* win = new ForgeWindow;
 	newFile ();
 	
+	loadPrimitiveInfo ();
+	
 	win->show ();
 	return app.exec ();
 }
--- a/src/types.h	Sat Jun 15 01:29:46 2013 +0300
+++ b/src/types.h	Sat Jun 15 04:20:44 2013 +0300
@@ -20,6 +20,7 @@
 #define TYPES_H
 
 #include <QString>
+#include <QObject>
 #include <vector>
 #include "common.h"
 

mercurial