Transformed primitive management into a new class PrimitiveManager that is a member of MainWindow

Tue, 16 Feb 2016 00:59:50 +0200

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Tue, 16 Feb 2016 00:59:50 +0200
changeset 1018
49358df9495b
parent 1017
fc1c13db9618
child 1019
a2e4286adb0d

Transformed primitive management into a new class PrimitiveManager that is a member of MainWindow

src/addObjectDialog.cpp file | annotate | diff | comparison | revisions
src/editmodes/circleMode.cpp file | annotate | diff | comparison | revisions
src/hierarchyelement.cpp file | annotate | diff | comparison | revisions
src/hierarchyelement.h file | annotate | diff | comparison | revisions
src/main.cpp file | annotate | diff | comparison | revisions
src/mainwindow.cpp file | annotate | diff | comparison | revisions
src/mainwindow.h file | annotate | diff | comparison | revisions
src/primitives.cpp file | annotate | diff | comparison | revisions
src/primitives.h file | annotate | diff | comparison | revisions
src/toolsets/filetoolset.cpp file | annotate | diff | comparison | revisions
--- a/src/addObjectDialog.cpp	Sun Feb 14 03:19:28 2016 +0200
+++ b/src/addObjectDialog.cpp	Tue Feb 16 00:59:50 2016 +0200
@@ -95,8 +95,13 @@
 			coordCount = 3;
 			tw_subfileList = new QTreeWidget();
 			tw_subfileList->setHeaderLabel (tr ("Primitives"));
-			populatePrimitivesTree (tw_subfileList,
-									(obj ? static_cast<LDSubfileReference*> (obj)->fileInfo()->name() : ""));
+
+			QString defaultname;
+
+			if (obj)
+				defaultname = static_cast<LDSubfileReference*> (obj)->fileInfo()->name();
+
+			g_win->primitives()->populateTreeWidget(tw_subfileList, defaultname);
 			connect (tw_subfileList, SIGNAL (itemSelectionChanged()), this, SLOT (slot_subfileTypeChanged()));
 			lb_subfileName = new QLabel ("File:");
 			le_subfileName = new QLineEdit;
--- a/src/editmodes/circleMode.cpp	Sun Feb 14 03:19:28 2016 +0200
+++ b/src/editmodes/circleMode.cpp	Tue Feb 16 00:59:50 2016 +0200
@@ -91,14 +91,14 @@
 	if (dist0 == dist1)
 	{
 		// If the radii are the same, there's no ring space to fill. Use a circle.
-		refFile = GetPrimitive (::Circle, segments, divisions, 0);
+		refFile = primitives()->getPrimitive(::Circle, segments, divisions, 0);
 		transform = getCircleDrawMatrix (dist0);
 		circleOrDisc = true;
 	}
 	else if (dist0 == 0 or dist1 == 0)
 	{
 		// If either radii is 0, use a disc.
-		refFile = GetPrimitive (::Disc, segments, divisions, 0);
+		refFile = primitives()->getPrimitive(::Disc, segments, divisions, 0);
 		transform = getCircleDrawMatrix ((dist0 != 0) ? dist0 : dist1);
 		circleOrDisc = true;
 	}
@@ -107,7 +107,7 @@
 		// The ring finder found a solution, use that. Add the component rings to the file.
 		for (const RingFinder::Component& cmp : g_RingFinder.bestSolution()->getComponents())
 		{
-			refFile = GetPrimitive (::Ring, segments, divisions, cmp.num);
+			refFile = primitives()->getPrimitive(::Ring, segments, divisions, cmp.num);
 			LDSubfileReference* ref = LDSpawn<LDSubfileReference>();
 			ref->setFileInfo (refFile);
 			ref->setTransform (getCircleDrawMatrix (cmp.scale));
@@ -132,8 +132,8 @@
 		templ.setCoordinate (localz, renderer()->getDepthValue());
 
 		// Calculate circle coords
-		MakeCircle (segments, divisions, dist0, c0);
-		MakeCircle (segments, divisions, dist1, c1);
+		primitives()->makeCircle(segments, divisions, dist0, c0);
+		primitives()->makeCircle(segments, divisions, dist1, c1);
 
 		for (int i = 0; i < segments; ++i)
 		{
--- a/src/hierarchyelement.cpp	Sun Feb 14 03:19:28 2016 +0200
+++ b/src/hierarchyelement.cpp	Tue Feb 16 00:59:50 2016 +0200
@@ -22,6 +22,8 @@
 #include "mainwindow.h"
 #include "guiutilities.h"
 
+
+
 HierarchyElement::HierarchyElement (QObject* parent) :
 	m_window (nullptr)
 {
@@ -36,7 +38,7 @@
 	if (m_window == nullptr)
 	{
 		m_window = g_win;
-		print ("WARNING: Hierarchy element instance %p should be in the hierarchy of a "
+		print ("WARNING: Hierarchy element instance %1 should be in the hierarchy of a "
 			"MainWindow but isn't.\n", this);
 	}
 
@@ -44,17 +46,30 @@
 	m_config = m_window->config();
 }
 
+
+
 GuiUtilities* HierarchyElement::guiUtilities() const
 {
 	return m_window->guiUtilities();
 }
 
+
+
 LDDocument* HierarchyElement::currentDocument()
 {
 	return m_window->currentDocument();
 }
 
+
+
 const LDObjectList& HierarchyElement::selectedObjects()
 {
 	return m_window->selectedObjects();
 }
+
+
+
+PrimitiveManager* HierarchyElement::primitives()
+{
+	return m_window->primitives();
+}
--- a/src/hierarchyelement.h	Sun Feb 14 03:19:28 2016 +0200
+++ b/src/hierarchyelement.h	Tue Feb 16 00:59:50 2016 +0200
@@ -25,6 +25,7 @@
 class GuiUtilities;
 class LDDocument;
 class DocumentManager;
+class PrimitiveManager;
 
 //
 // Objects that are to take part in the MainWindow's hierarchy multiple-inherit from this class to get a few useful
@@ -38,6 +39,7 @@
 	const LDObjectList& selectedObjects();
 	LDDocument* currentDocument();
 	GuiUtilities* guiUtilities() const;
+	PrimitiveManager* primitives();
 
 protected:
 	MainWindow* m_window;
--- a/src/main.cpp	Sun Feb 14 03:19:28 2016 +0200
+++ b/src/main.cpp	Tue Feb 16 00:59:50 2016 +0200
@@ -22,17 +22,16 @@
 #include <QFile>
 #include <QTextStream>
 #include <QDir>
-#include "mainwindow.h"
 #include "ldDocument.h"
 #include "miscallenous.h"
 #include "colors.h"
 #include "basics.h"
-#include "primitives.h"
 #include "glRenderer.h"
 #include "dialogs.h"
 #include "crashCatcher.h"
 #include "ldpaths.h"
 #include "documentmanager.h"
+#include "mainwindow.h"
 
 MainWindow* g_win = nullptr;
 Configuration* Config = nullptr;
@@ -59,7 +58,6 @@
 	initCrashCatcher();
 	initColors();
 	MainWindow* win = new MainWindow(configObject);
-	LoadPrimitives();
 	win->show();
 
 	// Process the command line
--- a/src/mainwindow.cpp	Sun Feb 14 03:19:28 2016 +0200
+++ b/src/mainwindow.cpp	Tue Feb 16 00:59:50 2016 +0200
@@ -68,6 +68,7 @@
 	QMainWindow (parent, flags),
 	m_config(config),
 	m_guiUtilities (new GuiUtilities (this)),
+	m_primitives(new PrimitiveManager(this)),
 	ui (*new Ui_MainWindow),
 	m_externalPrograms (nullptr),
 	m_settings (makeSettings (this)),
@@ -96,8 +97,8 @@
 	connect (m_tabs, SIGNAL (currentChanged(int)), this, SLOT (tabSelected()));
 	connect (m_tabs, SIGNAL (tabCloseRequested (int)), this, SLOT (closeTab (int)));
 
-	if (ActivePrimitiveScanner())
-		connect (ActivePrimitiveScanner(), SIGNAL (workDone()), this, SLOT (updatePrimitives()));
+	if (m_primitives->activeScanner())
+		connect (m_primitives->activeScanner(), SIGNAL (workDone()), this, SLOT (updatePrimitives()));
 	else
 		updatePrimitives();
 
@@ -177,6 +178,13 @@
 		dialog->show();
 		m_config.setFirstStart (false);
 	}
+
+	QMetaObject::invokeMethod (this, "finishInitialization", Qt::QueuedConnection);
+}
+
+void MainWindow::finishInitialization()
+{
+	m_primitives->loadPrimitives();
 }
 
 MainWindow::~MainWindow()
@@ -908,6 +916,14 @@
 
 // ---------------------------------------------------------------------------------------------------------------------
 //
+void errorPrompt (QWidget* parent, const QString& message)
+{
+	QMessageBox::critical (parent, MainWindow::tr ("Error"), message,
+		(QMessageBox::Close), QMessageBox::Close);
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
 void MainWindow::updateDocumentList()
 {
 	m_updatingTabs = true;
@@ -1031,6 +1047,13 @@
 
 // ---------------------------------------------------------------------------------------------------------------------
 //
+PrimitiveManager* MainWindow::primitives()
+{
+	return m_primitives;
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
 GLRenderer* MainWindow::renderer()
 {
 	return m_renderer;
@@ -1048,7 +1071,7 @@
 //
 void MainWindow::updatePrimitives()
 {
-	populatePrimitivesTree (ui.primitives);
+	m_primitives->populateTreeWidget(ui.primitives);
 }
 
 // ---------------------------------------------------------------------------------------------------------------------
@@ -1340,45 +1363,4 @@
 void ColorToolbarItem::setToolButton (QToolButton* value)
 {
 	m_toolButton = value;
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-//
-void populatePrimitivesTree (QTreeWidget* tw, QString const& selectByDefault)
-{
-	tw->clear();
-
-	for (PrimitiveCategory* cat : g_PrimitiveCategories)
-	{
-		PrimitiveTreeItem* parentItem = new PrimitiveTreeItem (tw, nullptr);
-		parentItem->setText (0, cat->name());
-		QList<QTreeWidgetItem*> subfileItems;
-
-		for (Primitive& prim : cat->prims)
-		{
-			PrimitiveTreeItem* item = new PrimitiveTreeItem (parentItem, &prim);
-			item->setText (0, format ("%1 - %2", prim.name, prim.title));
-			subfileItems << item;
-
-			// If this primitive is the one the current object points to,
-			// select it by default
-			if (selectByDefault == prim.name)
-				tw->setCurrentItem (item);
-		}
-
-		tw->addTopLevelItem (parentItem);
-	}
-}
-
-PrimitiveTreeItem::PrimitiveTreeItem (QTreeWidgetItem* parent, Primitive* info) :
-	QTreeWidgetItem (parent),
-	m_primitive (info) {}
-
-PrimitiveTreeItem::PrimitiveTreeItem (QTreeWidget* parent, Primitive* info) :
-	QTreeWidgetItem (parent),
-	m_primitive (info) {}
-
-Primitive* PrimitiveTreeItem::primitive() const
-{
-	return m_primitive;
-}
+}
\ No newline at end of file
--- a/src/mainwindow.h	Sun Feb 14 03:19:28 2016 +0200
+++ b/src/mainwindow.h	Tue Feb 16 00:59:50 2016 +0200
@@ -35,9 +35,9 @@
 class GLRenderer;
 class QComboBox;
 class QProgressBar;
-struct Primitive;
 class Toolset;
 class Configuration;
+class PrimitiveManager;
 
 class ColorToolbarItem
 {
@@ -97,6 +97,7 @@
 	class GuiUtilities* guiUtilities();
 	void loadShortcuts();
 	LDDocument* newDocument (bool cache = false);
+	PrimitiveManager* primitives();
 	GLRenderer* renderer();
 	void refresh();
 	void refreshObjectList();
@@ -138,6 +139,7 @@
 	Configuration& m_config;
 	class GuiUtilities* m_guiUtilities;
 	GLRenderer* m_renderer;
+	PrimitiveManager* m_primitives;
 	LDObjectList m_sel;
 	QList<ColorToolbarItem>	m_quickColors;
 	QList<QToolButton*>	m_colorButtons;
@@ -156,6 +158,7 @@
 	QMap<QAction*, QKeySequence> m_defaultShortcuts;
 
 private slots:
+	void finishInitialization();
 	void selectionChanged();
 	void recentFileClicked();
 	void quickColorClicked();
@@ -180,6 +183,7 @@
 
 // Displays an error prompt with the given message
 void Critical (const QString& message);
+void errorPrompt (QWidget *parent, const QString& message);
 
 // Takes in pairs of radio buttons and respective values and finds the first selected one.
 // Returns returns the value of the first found radio button that was checked by the user.
@@ -210,16 +214,4 @@
 	}
 }
 
-class PrimitiveTreeItem : public QTreeWidgetItem
-{
-public:
-	PrimitiveTreeItem (QTreeWidgetItem* parent, Primitive* info);
-	PrimitiveTreeItem (QTreeWidget* parent, Primitive* info);
-	Primitive* primitive() const;
-
-private:
-	Primitive* m_primitive;
-};
-
-void populatePrimitivesTree (QTreeWidget* tw, const QString& selectByDefault = QString());
 QSettings* makeSettings (QObject* parent = nullptr);
--- a/src/primitives.cpp	Sun Feb 14 03:19:28 2016 +0200
+++ b/src/primitives.cpp	Tue Feb 16 00:59:50 2016 +0200
@@ -28,10 +28,7 @@
 #include "ldpaths.h"
 #include "documentmanager.h"
 
-QList<PrimitiveCategory*> g_PrimitiveCategories;
-QList<Primitive> g_primitives;
-static PrimitiveScanner* g_activeScanner = nullptr;
-PrimitiveCategory* g_unmatched = nullptr;
+QList<PrimitiveCategory*> m_categories;
 
 static const QStringList g_radialNameRoots =
 {
@@ -43,19 +40,19 @@
 	"con"
 };
 
-PrimitiveScanner* ActivePrimitiveScanner()
+PrimitiveScanner* PrimitiveManager::activeScanner()
 {
-	return g_activeScanner;
+	return m_activeScanner;
 }
 
-QString getPrimitivesCfgPath()
+QString PrimitiveManager::getPrimitivesCfgPath() const
 {
 	return qApp->applicationDirPath() + DIRSLASH "prims.cfg";
 }
 
 // =============================================================================
 //
-void LoadPrimitives()
+void PrimitiveManager::loadPrimitives()
 {
 	// Try to load prims.cfg
 	QFile conf (getPrimitivesCfgPath());
@@ -63,7 +60,7 @@
 	if (not conf.open (QIODevice::ReadOnly))
 	{
 		// No prims.cfg, build it
-		PrimitiveScanner::start();
+		startScan();
 	}
 	else
 	{
@@ -85,16 +82,19 @@
 			Primitive info;
 			info.name = line.left (space);
 			info.title = line.mid (space + 1);
-			g_primitives << info;
+			m_primitives << info;
 		}
 
-		PrimitiveCategory::populateCategories();
-		print ("%1 primitives loaded.\n", g_primitives.size());
+		populateCategories();
+		print ("%1 primitives loaded.\n", m_primitives.size());
 	}
 }
 
 // =============================================================================
 //
+// TODO: replace with QDirIterator
+//
+static void GetRecursiveFilenames (QDir dir, QList<QString>& fnames) __attribute__((deprecated));
 static void GetRecursiveFilenames (QDir dir, QList<QString>& fnames)
 {
 	QFileInfoList flist = dir.entryInfoList (QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
@@ -110,51 +110,50 @@
 
 // =============================================================================
 //
-PrimitiveScanner::PrimitiveScanner (QObject* parent) :
-	QObject (parent),
-	m_i (0)
+PrimitiveScanner::PrimitiveScanner (PrimitiveManager* parent) :
+	QObject(parent),
+	HierarchyElement(parent),
+	m_manager(parent),
+	m_i(0)
 {
-	g_activeScanner = this;
 	QDir dir = LDPaths::primitivesDir();
 	m_baselen = dir.absolutePath().length();
-	GetRecursiveFilenames (dir, m_files);
-	emit starting (m_files.size());
-	print ("Scanning primitives...");
+	GetRecursiveFilenames(dir, m_files);
+	emit starting(m_files.size());
+	print("Scanning primitives...");
 }
 
-// =============================================================================
-//
-PrimitiveScanner::~PrimitiveScanner()
+const QList<Primitive>& PrimitiveScanner::scannedPrimitives() const
 {
-	g_activeScanner = nullptr;
+	return m_prims;
 }
 
 // =============================================================================
 //
 void PrimitiveScanner::work()
 {
-	int j = qMin (m_i + 100, m_files.size());
+	int max = qMin (m_i + 100, m_files.size());
 
-	for (; m_i < j; ++m_i)
+	for (; m_i < max; ++m_i)
 	{
-		QString fname = m_files[m_i];
-		QFile f (fname);
+		QString filename = m_files[m_i];
+		QFile file (filename);
 
-		if (not f.open (QIODevice::ReadOnly))
+		if (not file.open (QIODevice::ReadOnly))
 			continue;
 
 		Primitive info;
-		info.name = fname.mid (m_baselen + 1);  // make full path relative
+		info.name = filename.mid (m_baselen + 1);  // make full path relative
 		info.name.replace ('/', '\\');  // use DOS backslashes, they're expected
 		info.category = nullptr;
-		QByteArray titledata = f.readLine();
+		QByteArray titledata = file.readLine();
 
 		if (titledata != QByteArray())
 			info.title = QString::fromUtf8 (titledata);
 
 		info.title = info.title.simplified();
 
-		if (Q_LIKELY (info.title[0] == '0'))
+		if (info.title[0] == '0')
 		{
 			info.title.remove (0, 1);  // remove 0
 			info.title = info.title.simplified();
@@ -166,26 +165,22 @@
 	if (m_i == m_files.size())
 	{
 		// Done with primitives, now save to a config file
-		QString path = getPrimitivesCfgPath();
-		QFile conf (path);
+		QString path = m_manager->getPrimitivesCfgPath();
+		QFile configFile (path);
 
-		if (not conf.open (QIODevice::WriteOnly | QIODevice::Text))
-			Critical (format ("Couldn't write primitive list %1: %2",
-				path, conf.errorString()));
-		else
+		if (configFile.open (QIODevice::WriteOnly | QIODevice::Text))
 		{
 			for (Primitive& info : m_prims)
-				fprint (conf, "%1 %2\r\n", info.name, info.title);
+				fprint (configFile, "%1 %2\r\n", info.name, info.title);
 
-			conf.close();
+			configFile.close();
+		}
+		else
+		{
+			errorPrompt(m_window, format("Couldn't write primitive list %1: %2", path, configFile.errorString()));
 		}
 
-		g_primitives = m_prims;
-		PrimitiveCategory::populateCategories();
-		print ("%1 primitives scanned", g_primitives.size());
-		g_activeScanner = nullptr;
 		emit workDone();
-		deleteLater();
 	}
 	else
 	{
@@ -197,14 +192,27 @@
 
 // =============================================================================
 //
-void PrimitiveScanner::start()
+void PrimitiveManager::startScan()
 {
-	if (g_activeScanner)
-		return;
+	if (m_activeScanner == nullptr)
+	{
+		loadCategories();
+		m_activeScanner = new PrimitiveScanner(this);
+		m_activeScanner->work();
+		connect(m_activeScanner, &PrimitiveScanner::workDone, this, &PrimitiveManager::scanDone);
+	}
+}
 
-	PrimitiveCategory::loadCategories();
-	PrimitiveScanner* scanner = new PrimitiveScanner;
-	scanner->work();
+void PrimitiveManager::scanDone()
+{
+	if (m_activeScanner)
+	{
+		m_primitives = m_activeScanner->scannedPrimitives();
+		populateCategories();
+		print ("%1 primitives scanned", m_primitives.size());
+		delete m_activeScanner;
+		m_activeScanner = nullptr;
+	}
 }
 
 // =============================================================================
@@ -215,62 +223,70 @@
 
 // =============================================================================
 //
-void PrimitiveCategory::populateCategories()
+void PrimitiveManager::clearCategories()
+{
+	for (PrimitiveCategory* category : m_categories)
+		delete category;
+
+	m_categories.clear();
+}
+
+// =============================================================================
+//
+void PrimitiveManager::populateCategories()
 {
 	loadCategories();
 
-	for (PrimitiveCategory* cat : g_PrimitiveCategories)
-		cat->prims.clear();
+	for (PrimitiveCategory* category : m_categories)
+		category->primitives.clear();
 
-	for (Primitive& prim : g_primitives)
+	for (Primitive& primitive : m_primitives)
 	{
 		bool matched = false;
-		prim.category = nullptr;
+		primitive.category = nullptr;
 
 		// Go over the categories and their regexes, if and when there's a match,
 		// the primitive's category is set to the category the regex beloings to.
-		for (PrimitiveCategory* cat : g_PrimitiveCategories)
+		for (PrimitiveCategory* category : m_categories)
 		{
-			for (RegexEntry& entry : cat->regexes)
+			for (PrimitiveCategory::RegexEntry& entry : category->patterns)
 			{
 				switch (entry.type)
 				{
-					case EFilenameRegex:
-					{
-						// f-regex, check against filename
-						matched = entry.regex.exactMatch (prim.name);
-					} break;
+				case PrimitiveCategory::FilenamePattern:
+					// f-regex, check against filename
+					matched = entry.regex.exactMatch (primitive.name);
+					break;
 
-					case ETitleRegex:
-					{
-						// t-regex, check against title
-						matched = entry.regex.exactMatch (prim.title);
-					} break;
+				case PrimitiveCategory::TitlePattern:
+					// t-regex, check against title
+					matched = entry.regex.exactMatch (primitive.title);
+					break;
 				}
 
 				if (matched)
 				{
-					prim.category = cat;
+					primitive.category = category;
 					break;
 				}
 			}
 
-			// Drop out if a category was decided on.
-			if (prim.category)
+			// Drop off if a category was decided on.
+			if (primitive.category)
 				break;
 		}
 
 		// If there was a match, add the primitive to the category.
 		// Otherwise, add it to the list of unmatched primitives.
-		if (prim.category)
-			prim.category->prims << prim;
+		if (primitive.category)
+			primitive.category->primitives << primitive;
 		else
-			g_unmatched->prims << prim;
+			m_unmatched->primitives << primitive;
 	}
 
 	// Sort the categories. Note that we do this here because we need the existing
 	// order for regex matching.
-	qSort (g_PrimitiveCategories.begin(), g_PrimitiveCategories.end(),
+	qSort (m_categories.begin(), m_categories.end(),
 		[](PrimitiveCategory* const& a, PrimitiveCategory* const& b) -> bool
 		{
 			return a->name() < b->name();
@@ -279,12 +295,9 @@
 
 // =============================================================================
 //
-void PrimitiveCategory::loadCategories()
+void PrimitiveManager::loadCategories()
 {
-	for (PrimitiveCategory* cat : g_PrimitiveCategories)
-		delete cat;
-
-	g_PrimitiveCategories.clear();
+	clearCategories();
 	QString path = ":/data/primitive-categories.cfg";
 	QFile f (path);
 
@@ -294,57 +307,67 @@
 		return;
 	}
 
-	PrimitiveCategory* cat = nullptr;
+	PrimitiveCategory* category = nullptr;
 
 	while (not f.atEnd())
 	{
-		QString line = f.readLine();
-		int colon;
-
-		if (line.endsWith ("\n"))
-			line.chop (1);
+		QString line = QString::fromUtf8(f.readLine()).trimmed();
 
 		if (line.length() == 0 or line[0] == '#')
 			continue;
 
-		if ((colon = line.indexOf (":")) == -1)
+		int colon = line.indexOf (":");
+		if (colon == -1)
 		{
-			if (cat and cat->isValidToInclude())
-				g_PrimitiveCategories << cat;
+			if (category and category->isValidToInclude())
+			{
+				m_categories << category;
+			}
+			else if (category)
+			{
+				print (tr ("Warning: Category \"%1\" left without patterns"), category->name());
+				delete category;
+			}
 
-			cat = new PrimitiveCategory (line);
+			category = new PrimitiveCategory (line);
 		}
-		else if (cat)
+		else if (category)
 		{
-			QString cmd = line.left (colon);
-			RegexType type = EFilenameRegex;
+			QString typechar = line.left (colon);
+			PrimitiveCategory::PatternType type = PrimitiveCategory::FilenamePattern;
 
-			if (cmd == "f")
-				type = EFilenameRegex;
-			else if (cmd == "t")
-				type = ETitleRegex;
+			if (typechar == "f")
+			{
+				type = PrimitiveCategory::FilenamePattern;
+			}
+			else if (typechar == "t")
+			{
+				type = PrimitiveCategory::TitlePattern;
+			}
 			else
 			{
-				print (tr ("Warning: unknown command \"%1\" on line \"%2\""), cmd, line);
+				print (tr ("Warning: unknown pattern type \"%1\" on line \"%2\""), typechar, line);
 				continue;
 			}
 
 			QRegExp regex (line.mid (colon + 1));
-			RegexEntry entry = { regex, type };
-			cat->regexes << entry;
+			PrimitiveCategory::RegexEntry entry = { regex, type };
+			category->patterns << entry;
 		}
 		else
+		{
 			print ("Warning: Rules given before the first category name");
+		}
 	}
 
-	if (cat->isValidToInclude())
-		g_PrimitiveCategories << cat;
+	if (category->isValidToInclude())
+		m_categories << category;
 
 	// Add a category for unmatched primitives.
 	// Note: if this function is called the second time, g_unmatched has been
 	// deleted at the beginning of the function and is dangling at this point.
-	g_unmatched = new PrimitiveCategory (tr ("Other"));
-	g_PrimitiveCategories << g_unmatched;
+	m_unmatched = new PrimitiveCategory (tr ("Other"));
+	m_categories << m_unmatched;
 	f.close();
 }
 
@@ -352,14 +375,7 @@
 //
 bool PrimitiveCategory::isValidToInclude()
 {
-	if (regexes.isEmpty())
-	{
-		print (tr ("Warning: category \"%1\" left without patterns"), name());
-		deleteLater();
-		return false;
-	}
-
-	return true;
+	return not patterns.isEmpty();
 }
 
 QString PrimitiveCategory::name() const
@@ -369,28 +385,23 @@
 
 // =============================================================================
 //
-bool IsPrimitiveLoaderBusy()
-{
-	return g_activeScanner;
-}
-
-// =============================================================================
-//
-static double GetRadialPoint (int i, int divs, double (*func) (double))
+static double getRadialPoint (int i, int divs, double (*func) (double))
 {
 	return (*func) ((i * 2 * Pi) / divs);
 }
 
 // =============================================================================
 //
-void MakeCircle (int segs, int divs, double radius, QList<QLineF>& lines)
+// TODO: this doesn't really belong here.
+//
+void PrimitiveManager::makeCircle (int segs, int divs, double radius, QList<QLineF>& lines)
 {
 	for (int i = 0; i < segs; ++i)
 	{
-		double x0 = radius * GetRadialPoint (i, divs, cos),
-			x1 = radius * GetRadialPoint (i + 1, divs, cos),
-			z0 = radius * GetRadialPoint (i, divs, sin),
-			z1 = radius * GetRadialPoint (i + 1, divs, sin);
+		double x0 = radius * getRadialPoint (i, divs, cos),
+			x1 = radius * getRadialPoint (i + 1, divs, cos),
+			z0 = radius * getRadialPoint (i, divs, sin),
+			z1 = radius * getRadialPoint (i + 1, divs, sin);
 
 		lines << QLineF (QPointF (x0, z0), QPointF (x1, z1));
 	}
@@ -398,24 +409,24 @@
 
 // =============================================================================
 //
-LDObjectList MakePrimitive (PrimitiveType type, int segs, int divs, int num)
+LDObjectList PrimitiveManager::makePrimitiveBody (PrimitiveType type, int segs, int divs, int num)
 {
 	LDObjectList objs;
-	QList<int> condLineSegs;
+	QList<int> conditionalLineSegments;
 	QList<QLineF> circle;
 
-	MakeCircle (segs, divs, 1, circle);
+	makeCircle (segs, divs, 1, circle);
 
 	for (int i = 0; i < segs; ++i)
 	{
-		double x0 = circle[i].x1(),
-				   x1 = circle[i].x2(),
-				   z0 = circle[i].y1(),
-				   z1 = circle[i].y2();
+		double x0 = circle[i].x1();
+		double x1 = circle[i].x2();
+		double z0 = circle[i].y1();
+		double z1 = circle[i].y2();
 
 		switch (type)
 		{
-			case Circle:
+		case Circle:
 			{
 				Vertex v0 (x0, 0.0f, z0),
 				  v1 (x1, 0.0f, z1);
@@ -425,11 +436,12 @@
 				line->setVertex (1, v1);
 				line->setColor (EdgeColor);
 				objs << line;
-			} break;
+			}
+			break;
 
-			case Cylinder:
-			case Ring:
-			case Cone:
+		case Cylinder:
+		case Ring:
+		case Cone:
 			{
 				double x2, x3, z2, z3;
 				double y0, y1, y2, y3;
@@ -465,10 +477,10 @@
 					}
 				}
 
-				Vertex v0 (x0, y0, z0),
-					   v1 (x1, y1, z1),
-					   v2 (x2, y2, z2),
-					   v3 (x3, y3, z3);
+				Vertex v0 (x0, y0, z0);
+				Vertex v1 (x1, y1, z1);
+				Vertex v2 (x2, y2, z2);
+				Vertex v3 (x3, y3, z3);
 
 				LDQuad* quad (LDSpawn<LDQuad> (v0, v1, v2, v3));
 				quad->setColor (MainColor);
@@ -479,11 +491,12 @@
 				objs << quad;
 
 				if (type == Cylinder or type == Cone)
-					condLineSegs << i;
-			} break;
+					conditionalLineSegments << i;
+			}
+			break;
 
-			case Disc:
-			case DiscNeg:
+		case Disc:
+		case DiscNegative:
 			{
 				double x2, z2;
 
@@ -507,21 +520,22 @@
 				seg->setVertex (1, v1);
 				seg->setVertex (type == Disc ? 2 : 0, v2);
 				objs << seg;
-			} break;
+			}
+			break;
 		}
 	}
 
 	// If this is not a full circle, we need a conditional line at the other
 	// end, too.
-	if (segs < divs and condLineSegs.size() != 0)
-		condLineSegs << segs;
+	if (segs < divs and not conditionalLineSegments.isEmpty())
+		conditionalLineSegments << segs;
 
-	for (int i : condLineSegs)
+	for (int i : conditionalLineSegments)
 	{
-		Vertex v0 (GetRadialPoint (i, divs, cos), 0.0f, GetRadialPoint (i, divs, sin)),
-		  v1,
-		  v2 (GetRadialPoint (i + 1, divs, cos), 0.0f, GetRadialPoint (i + 1, divs, sin)),
-		  v3 (GetRadialPoint (i - 1, divs, cos), 0.0f, GetRadialPoint (i - 1, divs, sin));
+		Vertex v0 (getRadialPoint (i, divs, cos), 0.0f, getRadialPoint (i, divs, sin));
+		Vertex v1;
+		Vertex v2 (getRadialPoint (i + 1, divs, cos), 0.0f, getRadialPoint (i + 1, divs, sin));
+		Vertex v3 (getRadialPoint (i - 1, divs, cos), 0.0f, getRadialPoint (i - 1, divs, sin));
 
 		if (type == Cylinder)
 		{
@@ -549,36 +563,37 @@
 
 // =============================================================================
 //
-static QString PrimitiveTypeName (PrimitiveType type)
+QString PrimitiveManager::primitiveTypeName (PrimitiveType type)
 {
 	// Not translated as primitives are in English.
-	return type == Circle   ? "Circle" :
-		   type == Cylinder ? "Cylinder" :
-		   type == Disc     ? "Disc" :
-		   type == DiscNeg  ? "Disc Negative" :
-		   type == Ring     ? "Ring" : "Cone";
+	const char* names[] = { "Circle", "Cylinder", "Disc", "Disc Negative", "Ring", "Cone" };
+
+	if (type >= 0 and type < countof(names))
+		return names[type];
+	else
+		return "Unknown";
 }
 
 // =============================================================================
 //
-QString MakeRadialFileName (PrimitiveType type, int segs, int divs, int num)
+QString PrimitiveManager::makeRadialFileName (PrimitiveType type, int segs, int divs, int num)
 {
-	int numer = segs,
-			denom = divs;
+	int numerator = segs;
+	int denominator = divs;
 
 	// Simplify the fractional part, but the denominator must be at least 4.
-	simplify (numer, denom);
+	simplify (numerator, denominator);
 
-	if (denom < 4)
+	if (denominator < 4)
 	{
-		const int factor = 4 / denom;
-		numer *= factor;
-		denom *= factor;
+		const int factor = 4 / denominator;
+		numerator *= factor;
+		denominator *= factor;
 	}
 
 	// Compose some general information: prefix, fraction, root, ring number
 	QString prefix = (divs == LowResolution) ? "" : format ("%1/", divs);
-	QString frac = format ("%1-%2", numer, denom);
+	QString frac = format ("%1-%2", numerator, denominator);
 	QString root = g_radialNameRoots[type];
 	QString numstr = (type == Ring or type == Cone) ? format ("%1", num) : "";
 
@@ -593,51 +608,50 @@
 
 // =============================================================================
 //
-LDDocument* GeneratePrimitive (PrimitiveType type, int segs, int divs, int num)
+LDDocument* PrimitiveManager::generatePrimitive (PrimitiveType type, int segments, int divisions, int number)
 {
 	// Make the description
-	QString frac = QString::number ((float) segs / divs);
-	QString name = MakeRadialFileName (type, segs, divs, num);
-	QString descr;
+	QString fraction = QString::number ((float) segments / divisions);
+	QString name = makeRadialFileName (type, segments, divisions, number);
+	QString description;
 
 	// Ensure that there's decimals, even if they're 0.
-	if (frac.indexOf (".") == -1)
-		frac += ".0";
+	if (fraction.indexOf (".") == -1)
+		fraction += ".0";
 
 	if (type == Ring or type == Cone)
 	{
 		QString spacing =
-			(num < 10) ? "  " :
-			(num < 100) ? " "  : "";
+			(number < 10) ? "  " :
+			(number < 100) ? " "  : "";
 
-		descr = format ("%1 %2%3 x %4", PrimitiveTypeName (type), spacing, num, frac);
+		description = format ("%1 %2%3 x %4", primitiveTypeName (type), spacing, number, fraction);
 	}
 	else
-		descr = format ("%1 %2", PrimitiveTypeName (type), frac);
+		description = format ("%1 %2", primitiveTypeName (type), fraction);
 
 	// Prepend "Hi-Res" if 48/ primitive.
-	if (divs == HighResolution)
-		descr.insert (0, "Hi-Res ");
+	if (divisions == HighResolution)
+		description.insert (0, "Hi-Res ");
 
-	LDDocument* document = g_win->newDocument();
+	LDDocument* document = m_window->newDocument();
 	document->setDefaultName (name);
 
 	QString author = APPNAME;
 	QString license = "";
 
-	if (not Config->defaultName().isEmpty())
+	if (not m_config->defaultName().isEmpty())
 	{
 		license = PreferredLicenseText();
-		author = format ("%1 [%2]", Config->defaultName(), Config->defaultUser());
+		author = format ("%1 [%2]", m_config->defaultName(), m_config->defaultUser());
 	}
 
 	LDObjectList objs;
 
-	objs << LDSpawn<LDComment> (descr)
+	objs << LDSpawn<LDComment> (description)
 		 << LDSpawn<LDComment> (format ("Name: %1", name))
 		 << LDSpawn<LDComment> (format ("Author: %1", author))
-		 << LDSpawn<LDComment> (format ("!LDRAW_ORG Unofficial_%1Primitive",
-									  divs == HighResolution ?  "48_" : ""))
+		 << LDSpawn<LDComment> (format ("!LDRAW_ORG Unofficial_%1Primitive", divisions == HighResolution ?  "48_" : ""))
 		 << LDSpawn<LDComment> (license)
 		 << LDSpawn<LDEmpty>()
 		 << LDSpawn<LDBfc> (BfcStatement::CertifyCCW)
@@ -646,22 +660,25 @@
 	document->openForEditing();
 	document->history()->setIgnoring (false);
 	document->addObjects (objs);
-	document->addObjects (MakePrimitive (type, segs, divs, num));
+	document->addObjects (makePrimitiveBody (type, segments, divisions, number));
 	document->addHistoryStep();
 	return document;
 }
 
 // =============================================================================
 //
-LDDocument* GetPrimitive (PrimitiveType type, int segs, int divs, int num)
+// Gets a primitive by the given specs. If the primitive cannot be found, it will
+// be automatically generated.
+//
+LDDocument* PrimitiveManager::getPrimitive (PrimitiveType type, int segs, int divs, int num)
 {
-	QString name = MakeRadialFileName (type, segs, divs, num);
-	LDDocument* f = g_win->documents()->getDocumentByName (name);
+	QString name = makeRadialFileName (type, segs, divs, num);
+	LDDocument* document = m_window->documents()->getDocumentByName (name);
 
-	if (f)
-		return f;
+	if (document)
+		return document;
 
-	return GeneratePrimitive (type, segs, divs, num);
+	return generatePrimitive (type, segs, divs, num);
 }
 
 // =============================================================================
@@ -692,3 +709,58 @@
 	if (on and ui->sb_segs->value() == LowResolution)
 		ui->sb_segs->setValue (HighResolution);
 }
+
+// =============================================================================
+//
+PrimitiveManager::PrimitiveManager(QObject* parent) :
+	QObject(parent),
+	HierarchyElement(parent),
+	m_activeScanner(nullptr),
+	m_unmatched(nullptr) {}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+void PrimitiveManager::populateTreeWidget(QTreeWidget* tree, const QString& selectByDefault)
+{
+	tree->clear();
+
+	for (PrimitiveCategory* category : m_categories)
+	{
+		PrimitiveTreeItem* parentItem = new PrimitiveTreeItem (tree, nullptr);
+		parentItem->setText (0, category->name());
+		QList<QTreeWidgetItem*> subfileItems;
+
+		for (Primitive& prim : category->primitives)
+		{
+			PrimitiveTreeItem* item = new PrimitiveTreeItem (parentItem, &prim);
+			item->setText (0, format ("%1 - %2", prim.name, prim.title));
+			subfileItems << item;
+
+			// If this primitive is the one the current object points to,
+			// select it by default
+			if (selectByDefault == prim.name)
+				tree->setCurrentItem (item);
+		}
+
+		tree->addTopLevelItem (parentItem);
+	}
+}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+PrimitiveTreeItem::PrimitiveTreeItem (QTreeWidgetItem* parent, Primitive* info) :
+	QTreeWidgetItem (parent),
+	m_primitive (info) {}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+PrimitiveTreeItem::PrimitiveTreeItem (QTreeWidget* parent, Primitive* info) :
+	QTreeWidgetItem (parent),
+	m_primitive (info) {}
+
+// ---------------------------------------------------------------------------------------------------------------------
+//
+Primitive* PrimitiveTreeItem::primitive() const
+{
+	return m_primitive;
+}
--- a/src/primitives.h	Sun Feb 14 03:19:28 2016 +0200
+++ b/src/primitives.h	Tue Feb 16 00:59:50 2016 +0200
@@ -17,10 +17,10 @@
  */
 
 #pragma once
-#include "main.h"
-#include "basics.h"
 #include <QRegExp>
 #include <QDialog>
+#include <QTreeWidgetItem>
+#include "main.h"
 
 class LDDocument;
 class Ui_MakePrimUI;
@@ -28,9 +28,9 @@
 
 struct Primitive
 {
-	QString				name;
-	QString				title;
-	PrimitiveCategory*	category;
+	QString name;
+	QString title;
+	PrimitiveCategory* category;
 };
 
 class PrimitiveCategory : public QObject
@@ -38,28 +38,25 @@
 	Q_OBJECT
 
 public:
-	enum RegexType
+	enum PatternType
 	{
-		EFilenameRegex,
-		ETitleRegex
+		FilenamePattern,
+		TitlePattern
 	};
 
 	struct RegexEntry
 	{
 		QRegExp		regex;
-		RegexType	type;
+		PatternType	type;
 	};
 
-	QList<RegexEntry> regexes;
-	QList<Primitive> prims;
+	QList<RegexEntry> patterns;
+	QList<Primitive> primitives;
 
 	explicit PrimitiveCategory (QString name, QObject* parent = 0);
 	bool isValidToInclude();
 	QString name() const;
 
-	static void loadCategories();
-	static void populateCategories();
-
 private:
 	QString m_name;
 };
@@ -68,41 +65,38 @@
 // Worker object that scans the primitives folder for primitives and
 // builds an index of them.
 //
-class PrimitiveScanner : public QObject
+class PrimitiveScanner : public QObject, HierarchyElement
 {
 	Q_OBJECT
 
 public:
-	explicit			PrimitiveScanner (QObject* parent = 0);
-	virtual				~PrimitiveScanner();
-	static void			start();
+	PrimitiveScanner(PrimitiveManager* parent);
+	const QList<Primitive>& scannedPrimitives() const;
 
 public slots:
-	void				work();
+	void work();
 
 signals:
-	void				starting (int num);
-	void				workDone();
-	void				update (int i);
+	void starting(int num);
+	void workDone();
+	void update(int i);
 
 private:
-	QList<Primitive>	m_prims;
-	QStringList			m_files;
-	int					m_i;
-	int					m_baselen;
+	PrimitiveManager* m_manager;
+	QList<Primitive> m_prims;
+	QStringList m_files;
+	int m_i;
+	int m_baselen;
 };
 
-extern QList<PrimitiveCategory*> g_PrimitiveCategories;
-
-void LoadPrimitives();
-PrimitiveScanner* ActivePrimitiveScanner();
+extern QList<PrimitiveCategory*> m_categories;
 
 enum PrimitiveType
 {
 	Circle,
 	Cylinder,
 	Disc,
-	DiscNeg,
+	DiscNegative,
 	Ring,
 	Cone,
 };
@@ -120,11 +114,44 @@
 	void hiResToggled (bool on);
 };
 
-void MakeCircle (int segs, int divs, double radius, QList<QLineF>& lines);
-LDDocument* GeneratePrimitive (PrimitiveType type, int segs, int divs, int num);
+class PrimitiveManager : public QObject, HierarchyElement
+{
+	Q_OBJECT
+
+public:
+	PrimitiveManager(QObject* parent);
+
+	PrimitiveScanner* activeScanner();
+	LDDocument* generatePrimitive(PrimitiveType type, int segs, int divs, int num);
+	LDDocument* getPrimitive(PrimitiveType type, int segs, int divs, int num);
+	QString getPrimitivesCfgPath() const;
+	void loadPrimitives();
+	void makeCircle(int segs, int divs, double radius, QList<QLineF>& lines);
+	QString makeRadialFileName(PrimitiveType type, int segs, int divs, int num);
+	void populateTreeWidget(QTreeWidget* tree, const QString& selectByDefault = QString());
+	QString primitiveTypeName(PrimitiveType type);
+	Q_SLOT void scanDone();
+	void startScan();
 
-// Gets a primitive by the given specs. If the primitive cannot be found, it will
-// be automatically generated.
-LDDocument* GetPrimitive (PrimitiveType type, int segs, int divs, int num);
+private:
+	QList<PrimitiveCategory*> m_categories;
+	PrimitiveScanner* m_activeScanner;
+	QList<Primitive> m_primitives;
+	PrimitiveCategory* m_unmatched;
 
-QString MakeRadialFileName (PrimitiveType type, int segs, int divs, int num);
+	LDObjectList makePrimitiveBody (PrimitiveType type, int segs, int divs, int num);
+	void loadCategories();
+	void populateCategories();
+	void clearCategories();
+};
+
+class PrimitiveTreeItem : public QTreeWidgetItem
+{
+public:
+	PrimitiveTreeItem (QTreeWidgetItem* parent, Primitive* info);
+	PrimitiveTreeItem (QTreeWidget* parent, Primitive* info);
+	Primitive* primitive() const;
+
+private:
+	Primitive* m_primitive;
+};
--- a/src/toolsets/filetoolset.cpp	Sun Feb 14 03:19:28 2016 +0200
+++ b/src/toolsets/filetoolset.cpp	Tue Feb 16 00:59:50 2016 +0200
@@ -172,7 +172,7 @@
 
 void FileToolset::scanPrimitives()
 {
-	PrimitiveScanner::start();
+	primitives()->startScan();
 }
 
 void FileToolset::openSubfiles()
@@ -200,24 +200,25 @@
 
 void FileToolset::makePrimitive()
 {
-	PrimitivePrompt* dlg = new PrimitivePrompt (m_window);
+	PrimitivePrompt* dialog = new PrimitivePrompt (m_window);
 
-	if (not dlg->exec())
+	if (not dialog->exec())
 		return;
 
-	int segs = dlg->ui->sb_segs->value();
-	int divs = dlg->ui->cb_hires->isChecked() ? HighResolution : LowResolution;
-	int num = dlg->ui->sb_ringnum->value();
+	int segs = dialog->ui->sb_segs->value();
+	int divs = dialog->ui->cb_hires->isChecked() ? HighResolution : LowResolution;
+	int num = dialog->ui->sb_ringnum->value();
+
 	PrimitiveType type =
-		dlg->ui->rb_circle->isChecked()   ? Circle :
-		dlg->ui->rb_cylinder->isChecked() ? Cylinder :
-		dlg->ui->rb_disc->isChecked()     ? Disc :
-		dlg->ui->rb_ndisc->isChecked()    ? DiscNeg :
-		dlg->ui->rb_ring->isChecked()     ? Ring : Cone;
+		dialog->ui->rb_circle->isChecked()   ? Circle :
+		dialog->ui->rb_cylinder->isChecked() ? Cylinder :
+		dialog->ui->rb_disc->isChecked()     ? Disc :
+		dialog->ui->rb_ndisc->isChecked()    ? DiscNegative :
+		dialog->ui->rb_ring->isChecked()     ? Ring : Cone;
 
-	LDDocument* f = GeneratePrimitive (type, segs, divs, num);
-	f->openForEditing();
-	m_window->save (f, false);
+	LDDocument* primitive = primitives()->generatePrimitive(type, segs, divs, num);
+	primitive->openForEditing();
+	m_window->save(primitive, false);
 }
 
 // These are not exactly file tools but I don't want to make another toolset just for 3 very small actions

mercurial