Commit configuration rework (doesn't work yet, more than most probably doesn't compile either)

Mon, 31 Aug 2015 04:57:16 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Mon, 31 Aug 2015 04:57:16 +0300
changeset 970
c8aae45afd85
parent 969
b1742ee91d5b
child 971
c00f9665a9f8

Commit configuration rework (doesn't work yet, more than most probably doesn't compile either)

CMakeLists.txt file | annotate | diff | comparison | revisions
src/configDialog.cpp file | annotate | diff | comparison | revisions
src/configDialog.h file | annotate | diff | comparison | revisions
src/configuration.h file | annotate | diff | comparison | revisions
src/dialogs.cpp file | annotate | diff | comparison | revisions
src/dialogs/colorselector.cpp file | annotate | diff | comparison | revisions
src/dialogs/colorselector.h file | annotate | diff | comparison | revisions
src/dialogs/newpartdialog.cpp file | annotate | diff | comparison | revisions
src/dialogs/newpartdialog.h file | annotate | diff | comparison | revisions
src/editmodes/abstractEditMode.cpp file | annotate | diff | comparison | revisions
src/editmodes/abstractEditMode.h file | annotate | diff | comparison | revisions
src/glCompiler.cpp file | annotate | diff | comparison | revisions
src/glCompiler.h file | annotate | diff | comparison | revisions
src/glRenderer.cpp file | annotate | diff | comparison | revisions
src/glRenderer.h file | annotate | diff | comparison | revisions
src/hierarchyelement.cpp file | annotate | diff | comparison | revisions
src/hierarchyelement.h file | annotate | diff | comparison | revisions
src/ldDocument.cpp file | annotate | diff | comparison | revisions
src/ldObject.cpp file | annotate | diff | comparison | revisions
src/ldpaths.cpp file | annotate | diff | comparison | revisions
src/ldpaths.h file | annotate | diff | comparison | revisions
src/macros.h file | annotate | diff | comparison | revisions
src/main.cpp file | annotate | diff | comparison | revisions
src/main.h file | annotate | diff | comparison | revisions
src/mainwindow.cpp file | annotate | diff | comparison | revisions
src/mainwindow.h file | annotate | diff | comparison | revisions
src/miscallenous.cpp file | annotate | diff | comparison | revisions
src/miscallenous.h file | annotate | diff | comparison | revisions
src/partDownloader.cpp file | annotate | diff | comparison | revisions
src/partDownloader.h file | annotate | diff | comparison | revisions
src/primitives.cpp file | annotate | diff | comparison | revisions
src/toolsets/algorithmtoolset.cpp file | annotate | diff | comparison | revisions
src/toolsets/extprogramtoolset.cpp file | annotate | diff | comparison | revisions
src/toolsets/extprogramtoolset.h file | annotate | diff | comparison | revisions
src/toolsets/filetoolset.cpp file | annotate | diff | comparison | revisions
src/toolsets/movetoolset.cpp file | annotate | diff | comparison | revisions
src/toolsets/movetoolset.h file | annotate | diff | comparison | revisions
src/toolsets/toolset.cpp file | annotate | diff | comparison | revisions
src/toolsets/toolset.h file | annotate | diff | comparison | revisions
src/toolsets/viewtoolset.cpp file | annotate | diff | comparison | revisions
tools/updaterevision.py file | annotate | diff | comparison | revisions
updaterevision.py file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Sun Aug 30 17:20:55 2015 +0300
+++ b/CMakeLists.txt	Mon Aug 31 04:57:16 2015 +0300
@@ -6,7 +6,6 @@
 ######################################################################
 
 project (ldforge)
-add_subdirectory (codegen)
 cmake_minimum_required (VERSION 2.6)
 
 option (TRANSPARENT_DIRECT_COLORS "Enables non-standard transparent direct colors" OFF)
@@ -24,10 +23,15 @@
 
 find_package (OpenGL REQUIRED)
 
-get_target_property (CODEGEN_EXE codegen LOCATION)
+add_custom_target (revision_check ALL
+	COMMAND python "${CMAKE_SOURCE_DIR}/tools/updaterevision.py" hginfo.h
+	WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
 
-add_custom_target (revision_check ALL
-	COMMAND python "${CMAKE_SOURCE_DIR}/updaterevision.py" hginfo.h
+add_custom_target (config_collection ALL
+	COMMAND python
+		"${CMAKE_SOURCE_DIR}/tools/configcollector.py"
+		--header configurationvaluebag.h
+		--source configurationvaluebag.cpp
 	WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
 
 include_directories (${QT_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR})
@@ -148,11 +152,6 @@
 	src/dialogs/openprogressdialog.ui
 )
 
-add_custom_target (codegeneration ALL
-	COMMAND ${CODEGEN_EXE} ${LDFORGE_SOURCES} ${CMAKE_BINARY_DIR}/configuration.inc
-	WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
-	DEPENDS codegen)
-
 set (LDFORGE_RESOURCES ldforge.qrc)
 # set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lGLU")
 
@@ -208,5 +207,5 @@
 	)
 endif()
 
-add_dependencies (ldforge revision_check codegeneration)
+add_dependencies (ldforge revision_check config_collection)
 install (TARGETS ldforge RUNTIME DESTINATION bin)
--- a/src/configDialog.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/configDialog.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -39,20 +39,6 @@
 #include "glRenderer.h"
 #include "ui_config.h"
 
-EXTERN_CFGENTRY (String, YtruderPath)
-EXTERN_CFGENTRY (String, RectifierPath)
-EXTERN_CFGENTRY (String, IntersectorPath)
-EXTERN_CFGENTRY (String, CovererPath)
-EXTERN_CFGENTRY (String, IsecalcPath)
-EXTERN_CFGENTRY (String, Edger2Path)
-EXTERN_CFGENTRY (Bool, YtruderUsesWine)
-EXTERN_CFGENTRY (Bool, RectifierUsesWine)
-EXTERN_CFGENTRY (Bool, IntersectorUsesWine)
-EXTERN_CFGENTRY (Bool, CovererUsesWine)
-EXTERN_CFGENTRY (Bool, IsecalcUsesWine)
-EXTERN_CFGENTRY (Bool, Edger2UsesWine)
-EXTERN_CFGENTRY (String, QuickColorToolbar)
-
 const char* g_extProgPathFilter =
 #ifdef _WIN32
 	"Applications (*.exe)(*.exe);;"
@@ -61,42 +47,15 @@
 
 //
 //
-static struct LDExtProgInfo
-{
-	QString const	name;
-	QString const	iconname;
-	QString* const	path;
-	QLineEdit*		input;
-	QPushButton*	setPathButton;
-	bool* const		wine;
-	QCheckBox*		wineBox;
-} g_LDExtProgInfo[] =
-{
-#ifndef _WIN32
-# define EXTPROG(NAME, LOWNAME) { #NAME, #LOWNAME, &cfg::NAME##Path, null, null, \
-	&cfg::NAME##UsesWine, null },
-#else
-# define EXTPROG(NAME, LOWNAME) { #NAME, #LOWNAME, &cfg::NAME##Path, null, null, null, null },
-#endif
-	EXTPROG (Ytruder, ytruder)
-	EXTPROG (Rectifier, rectifier)
-	EXTPROG (Intersector, intersector)
-	EXTPROG (Isecalc, isecalc)
-	EXTPROG (Coverer, coverer)
-	EXTPROG (Edger2, edger2)
-#undef EXTPROG
-};
-
-//
-//
-ConfigDialog::ConfigDialog (ConfigDialog::Tab deftab, QWidget* parent, Qt::WindowFlags f) :
-	QDialog (parent, f)
+ConfigDialog::ConfigDialog (QWidget* parent, ConfigDialog::Tab defaulttab, Qt::WindowFlags f) :
+	QDialog (parent, f),
+	HierarchyElement (parent)
 {
 	ui = new Ui_ConfigUI;
 	ui->setupUi (this);
 
 	// Set defaults
-	m_applyToWidgetOptions ([&](QWidget* wdg, AbstractConfigEntry* conf)
+	applyToWidgetOptions ([&](QWidget* wdg, AbstractConfigEntry* conf)
 	{
 		QVariant value (conf->toVariant());
 		QLineEdit* le;
@@ -137,20 +96,17 @@
 		}
 	});
 
-	if (g_win)
+	m_window->applyToActions ([&](QAction* act)
 	{
-		g_win->applyToActions ([&](QAction* act)
-		{
-			addShortcut (act);
-		});
-	}
+		addShortcut (act);
+	});
 
 	ui->shortcutsList->setSortingEnabled (true);
 	ui->shortcutsList->sortItems();
 	quickColors = LoadQuickColorList();
 	updateQuickColorList();
 	initExtProgs();
-	selectPage (deftab);
+	selectPage (defaulttab);
 	connect (ui->shortcut_set, SIGNAL (clicked()), this, SLOT (slot_setShortcut()));
 	connect (ui->shortcut_reset, SIGNAL (clicked()), this, SLOT (slot_resetShortcut()));
 	connect (ui->shortcut_clear, SIGNAL (clicked()), this, SLOT (slot_clearShortcut()));
@@ -210,33 +166,36 @@
 	QGridLayout* pathsLayout = new QGridLayout;
 	int row = 0;
 
-	for (LDExtProgInfo& info : g_LDExtProgInfo)
+	for (int i = 0; i < NumExternalPrograms; ++i)
 	{
-		QLabel* icon = new QLabel,
-		*progLabel = new QLabel (info.name);
+		ExtProgramType program = (ExtProgramType) i;
+		ExternalProgramWidgets& widgets = m_externalProgramWidgets[i];
+		QString name = m_window->externalPrograms()->externalProgramName (program);
+		QLabel* icon = new QLabel;
+		QLabel* progLabel = new QLabel (name);
 		QLineEdit* input = new QLineEdit;
 		QPushButton* setPathButton = new QPushButton;
 
-		icon->setPixmap (GetIcon (info.iconname));
+		icon->setPixmap (GetIcon (name.toLower()));
 		input->setText (*info.path);
 		setPathButton->setIcon (GetIcon ("folder"));
-		info.input = input;
-		info.setPathButton = setPathButton;
-
+		widgets.input = input;
+		widgets.setPathButton = setPathButton;
+		widgets.wineBox = nullptr;
 		connect (setPathButton, SIGNAL (clicked()), this, SLOT (slot_setExtProgPath()));
-
 		pathsLayout->addWidget (icon, row, 0);
 		pathsLayout->addWidget (progLabel, row, 1);
 		pathsLayout->addWidget (input, row, 2);
 		pathsLayout->addWidget (setPathButton, row, 3);
 
-		if (info.wine != null)
+#ifdef Q_OS_UNIX
 		{
 			QCheckBox* wineBox = new QCheckBox ("Wine");
-			wineBox->setChecked (*info.wine);
-			info.wineBox = wineBox;
+			wineBox->setChecked (m_window->externalPrograms()->programUsesWine (program));
+			widgets.wineBox = wineBox;
 			pathsLayout->addWidget (wineBox, row, 4);
 		}
+#endif
 
 		++row;
 	}
@@ -244,7 +203,7 @@
 	ui->extProgs->setLayout (pathsLayout);
 }
 
-void ConfigDialog::m_applyToWidgetOptions (std::function<void (QWidget*, AbstractConfigEntry*)> func)
+void ConfigDialog::applyToWidgetOptions (std::function<void (QWidget*, AbstractConfigEntry*)> func)
 {
 	// Apply configuration
 	for (QWidget* widget : findChildren<QWidget*>())
@@ -253,7 +212,7 @@
 			continue;
 
 		QString confname (widget->objectName().mid (strlen ("config")));
-		AbstractConfigEntry* conf (Config::FindByName (confname));
+		AbstractConfigEntry* conf (m_config->findByName (confname));
 
 		if (conf == null)
 		{
@@ -270,7 +229,7 @@
 //
 void ConfigDialog::applySettings()
 {
-	m_applyToWidgetOptions ([&](QWidget* widget, AbstractConfigEntry* conf)
+	applyToWidgetOptions ([&](QWidget* widget, AbstractConfigEntry* conf)
 	{
 		QVariant value (conf->toVariant());
 		QLineEdit* le;
@@ -299,18 +258,19 @@
 	});
 
 	// Rebuild the quick color toolbar
-	if (g_win)
-		g_win->setQuickColors (quickColors);
-
-	cfg::QuickColorToolbar = quickColorString();
+	m_window->setQuickColors (quickColors);
+	m_config->quickColorToolbar = quickColorString();
 
 	// Ext program settings
-	for (const LDExtProgInfo& info : g_LDExtProgInfo)
+	for (int i = 0; i < NumExternalPrograms; ++i)
 	{
-		*info.path = info.input->text();
+		ExtProgramType program = (ExtProgramType) i;
+		ExtProgramToolset* toolset = m_window->externalPrograms();
+		ExternalProgramWidgets& widgets = m_externalProgramWidgets[i];
+		toolset->getPathSetting (program) = widgets.input->text();
 
-		if (info.wine != null)
-			*info.wine = info.wineBox->isChecked();
+		if (widgets.wineBox)
+			toolset->getWineSetting (program) = widgets.wineBox->isChecked();
 	}
 
 	// Apply shortcuts
@@ -324,12 +284,9 @@
 	LDDocument::current()->reloadAllSubfiles();
 	LoadLogoStuds();
 
-	if (g_win)
-	{
-		g_win->R()->setBackground();
-		g_win->doFullRefresh();
-		g_win->updateDocumentList();
-	}
+	m_window->R()->setBackground();
+	m_window->doFullRefresh();
+	m_window->updateDocumentList();
 }
 
 //
@@ -624,26 +581,29 @@
 //
 void ConfigDialog::slot_setExtProgPath()
 {
-	const LDExtProgInfo* info = null;
+	ExtProgramType program = NumExternalPrograms;
 
-	for (const LDExtProgInfo& it : g_LDExtProgInfo)
+	for (int i = 0; i < NumExternalPrograms; ++i)
 	{
-		if (it.setPathButton == sender())
+		if (m_externalProgramWidgets[i].setPathButton == sender())
 		{
-			info = &it;
+			program = (ExtProgramType) i;
 			break;
 		}
 	}
 
-	if (info != null)
+	if (program != NumExternalPrograms)
 	{
+		ExtProgramToolset* toolset = m_window->externalPrograms();
+		ExternalProgramWidgets& widgets = m_externalProgramWidgets[program];
 		QString filepath = QFileDialog::getOpenFileName (this,
-			format ("Path to %1", info->name), *info->path, g_extProgPathFilter);
+			format ("Path to %1", toolset->externalProgramName (program)),
+			widgets.input->text(), g_extProgPathFilter);
 	
 		if (filepath.isEmpty())
 			return;
 	
-		info->input->setText (filepath);
+		widgets.input->setText (filepath);
 	}
 }
 
--- a/src/configDialog.h	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/configDialog.h	Mon Aug 31 04:57:16 2015 +0300
@@ -18,6 +18,7 @@
 
 #pragma once
 #include "mainwindow.h"
+#include "toolsets/extprogramtoolset.h"
 #include <QDialog>
 
 class Ui_ConfigUI;
@@ -35,8 +36,15 @@
 		QListWidgetItem (view, type) {}
 };
 
+struct ExternalProgramWidgets
+{
+	class QLineEdit* input;
+	class QPushButton* setPathButton;
+	class QCheckBox* wineBox;
+};
+
 // =============================================================================
-class ConfigDialog : public QDialog
+class ConfigDialog : public QDialog, public HierarchyElement
 {
 	Q_OBJECT
 
@@ -53,7 +61,7 @@
 		DownloadTab
 	};
 
-	explicit ConfigDialog (Tab deftab = InterfaceTab, QWidget* parent = null, Qt::WindowFlags f = 0);
+	explicit ConfigDialog (QWidget* parent = nullptr, Tab defaulttab = (Tab) 0, Qt::WindowFlags f = 0);
 	virtual ~ConfigDialog();
 
 	QList<LDQuickColor> quickColors;
@@ -62,6 +70,7 @@
 	Ui_ConfigUI* ui;
 	QList<QListWidgetItem*> quickColorItems;
 	QMap<QPushButton*, QColor> m_buttonColors;
+	ExternalProgramWidgets m_externalProgramWidgets[NumExternalPrograms];
 
 	void applySettings();
 	void addShortcut (QAction* act);
@@ -73,7 +82,7 @@
 	QListWidgetItem* getSelectedQuickColor();
 	QList<ShortcutListItem*> getShortcutSelection();
 	void initExtProgs();
-	void m_applyToWidgetOptions (std::function<void (QWidget*, AbstractConfigEntry*)> func);
+	void applyToWidgetOptions (std::function<void (QWidget*, AbstractConfigEntry*)> func);
 
 private slots:
 	void setButtonColor();
--- a/src/configuration.h	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/configuration.h	Mon Aug 31 04:57:16 2015 +0300
@@ -26,9 +26,6 @@
 class QSettings;
 class AbstractConfigEntry;
 
-#define CFGENTRY(T, NAME, DEFAULT) namespace cfg { AbstractConfigEntry::T##Type NAME; }
-#define EXTERN_CFGENTRY(T, NAME) namespace cfg { extern AbstractConfigEntry::T##Type NAME; }
-
 namespace Config
 {
 	void Initialize();
--- a/src/dialogs.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/dialogs.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -42,7 +42,6 @@
 #include "ui_about.h"
 
 extern const char* g_extProgPathFilter;
-EXTERN_CFGENTRY (String, LDrawPath)
 
 // =============================================================================
 // =============================================================================
--- a/src/dialogs/colorselector.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/dialogs/colorselector.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -31,11 +31,9 @@
 
 enum { NUM_COLUMNS = 16 };
 
-EXTERN_CFGENTRY (String, MainColor)
-EXTERN_CFGENTRY (Float, MainColorAlpha)
-
-ColorSelector::ColorSelector (LDColor defaultvalue, QWidget* parent) :
+ColorSelector::ColorSelector (QWidget* parent, LDColor defaultvalue) :
 	QDialog (parent),
+	HierarchyElement (parent),
 	ui (*new Ui_ColorSelUi)
 {
 	m_firstResize = true;
@@ -57,8 +55,8 @@
 
 			if (ldcolor == MainColor)
 			{
-				color = QColor (cfg::MainColor);
-				color.setAlphaF (cfg::MainColorAlpha);
+				color = QColor (m_config->mainColor);
+				color.setAlphaF (m_config->mainColorAlpha);
 			}
 
 			QString color2name (Luma (color) < 80 ? "white" : "black");
@@ -178,9 +176,9 @@
 		selectDirectColor (selection().faceColor());
 }
 
-bool ColorSelector::selectColor (LDColor& val, LDColor defval, QWidget* parent)
+bool ColorSelector::selectColor (QWidget* parent, LDColor& val, LDColor defaultvalue)
 {
-	ColorSelector dlg (defval, parent);
+	ColorSelector dlg (parent, defaultvalue);
 
 	if (dlg.exec() and dlg.selection().isValid())
 	{
--- a/src/dialogs/colorselector.h	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/dialogs/colorselector.h	Mon Aug 31 04:57:16 2015 +0300
@@ -21,15 +21,15 @@
 #include "../main.h"
 #include "../colors.h"
 
-class ColorSelector : public QDialog
+class ColorSelector : public QDialog, public HierarchyElement
 {
 	Q_OBJECT
 	PROPERTY (private, LDColor, selection, setSelection, STOCK_WRITE)
 
 public:
-	explicit ColorSelector (LDColor defaultvalue = LDColor::nullColor(), QWidget* parent = null);
+	explicit ColorSelector (QWidget* parent, LDColor defaultvalue = LDColor::nullColor());
 	virtual ~ColorSelector();
-	static bool selectColor (LDColor& val, LDColor defval = LDColor::nullColor(), QWidget* parent = null);
+	static bool selectColor (QWidget* parent, LDColor& val, LDColor defval = LDColor::nullColor());
 
 private:
 	class Ui_ColorSelUi& ui;
--- a/src/dialogs/newpartdialog.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/dialogs/newpartdialog.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -20,26 +20,24 @@
 #include <QRadioButton>
 #include <QCheckBox>
 #include "../ldDocument.h"
+#include "../mainwindow.h"
 #include "newpartdialog.h"
 #include "ui_newpartdialog.h"
 
-EXTERN_CFGENTRY (String, DefaultName)
-EXTERN_CFGENTRY (String, DefaultUser)
-EXTERN_CFGENTRY (Bool, UseCALicense)
-
 NewPartDialog::NewPartDialog (QWidget *parent) :
 	QDialog (parent),
+	HierarchyElement (parent),
 	ui (*new Ui_NewPart)
 {
 	ui.setupUi (this);
 
-	QString authortext = cfg::DefaultName;
+	QString authortext = m_config->defaultName;
 
-	if (not cfg::DefaultUser.isEmpty())
-		authortext.append (format (" [%1]", cfg::DefaultUser));
+	if (not m_config->defaultUser.isEmpty())
+		authortext.append (format (" [%1]", m_config->defaultUser));
 
 	ui.author->setText (authortext);
-	ui.useCaLicense->setChecked (cfg::UseCALicense);
+	ui.useCaLicense->setChecked (m_config->useCaLicense);
 }
 
 BFCStatement NewPartDialog::getWinding() const
--- a/src/dialogs/newpartdialog.h	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/dialogs/newpartdialog.h	Mon Aug 31 04:57:16 2015 +0300
@@ -21,11 +21,11 @@
 #include "../main.h"
 #include "../ldObject.h"
 
-class NewPartDialog : public QDialog
+class NewPartDialog : public QDialog, HierarchyElement
 {
 	Q_OBJECT
 public:
-	NewPartDialog (QWidget *parent = nullptr);
+	NewPartDialog (QWidget *parent);
 
 	QString author() const;
 	void fillHeader (LDDocument* newdoc) const;
--- a/src/editmodes/abstractEditMode.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/editmodes/abstractEditMode.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -28,10 +28,12 @@
 #include "../mainwindow.h"
 #include "../glRenderer.h"
 
-CFGENTRY (Bool, DrawLineLengths, true)
-CFGENTRY (Bool, DrawAngles, false)
+ConfigOption (bool DrawLineLengths = true)
+ConfigOption (bool DrawAngles = false)
 
 AbstractEditMode::AbstractEditMode (GLRenderer* renderer) :
+	QObject (renderer),
+	HierarchyElement (renderer),
 	m_renderer (renderer) {}
 
 AbstractEditMode::~AbstractEditMode() {}
@@ -40,12 +42,12 @@
 {
 	switch (type)
 	{
-		case EditModeType::Select: return new SelectMode (renderer);
-		case EditModeType::Draw: return new DrawMode (renderer);
-		case EditModeType::Rectangle: return new RectangleMode (renderer);
-		case EditModeType::Circle: return new CircleMode (renderer);
-		case EditModeType::MagicWand: return new MagicWandMode (renderer);
-		case EditModeType::LinePath: return new LinePathMode (renderer);
+	case EditModeType::Select: return new SelectMode (renderer);
+	case EditModeType::Draw: return new DrawMode (renderer);
+	case EditModeType::Rectangle: return new RectangleMode (renderer);
+	case EditModeType::Circle: return new CircleMode (renderer);
+	case EditModeType::MagicWand: return new MagicWandMode (renderer);
+	case EditModeType::LinePath: return new LinePathMode (renderer);
 	}
 
 	throw std::logic_error ("bad type given to AbstractEditMode::createByType");
@@ -70,7 +72,7 @@
 	// Clear the selection when beginning to draw.
 	CurrentDocument()->clearSelection();
 
-	g_win->updateSelection();
+	m_window->updateSelection();
 	m_drawedVerts.clear();
 }
 
@@ -161,7 +163,7 @@
 
 void AbstractDrawMode::finishDraw (LDObjectList const& objs)
 {
-	int pos = g_win->getInsertionPoint();
+	int pos = m_window->getInsertionPoint();
 
 	if (objs.size() > 0)
 	{
@@ -171,8 +173,8 @@
 			renderer()->compileObject (obj);
 		}
 
-		g_win->refresh();
-		g_win->endAction();
+		m_window->refresh();
+		m_window->endAction();
 	}
 
 	m_drawedVerts.clear();
@@ -181,7 +183,7 @@
 void AbstractDrawMode::drawLength (QPainter &painter, const Vertex &v0, const Vertex &v1,
 	const QPointF& v0p, const QPointF& v1p) const
 {
-	if (not cfg::DrawLineLengths)
+	if (not m_config->drawLineLengths)
 		return;
 
 	const QString label = QString::number ((v1 - v0).length());
@@ -228,7 +230,7 @@
 			if (withlengths)
 				drawLength (painter, poly3d[i], poly3d[j], poly[i], poly[j]);
 
-			if (withangles and cfg::DrawAngles)
+			if (withangles and m_config->drawAngles)
 			{
 				QLineF l0 (poly[h], poly[i]),
 					l1 (poly[i], poly[j]);
--- a/src/editmodes/abstractEditMode.h	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/editmodes/abstractEditMode.h	Mon Aug 31 04:57:16 2015 +0300
@@ -34,9 +34,9 @@
 	LinePath,
 };
 
-class AbstractEditMode
+class AbstractEditMode : public QObject, public HierarchyElement
 {
-	GLRenderer* m_renderer;
+	Q_OBJECT
 
 public:
 	struct MouseEventData
@@ -61,6 +61,9 @@
 	virtual bool			keyReleased (QKeyEvent*) { return false; }
 
 	static AbstractEditMode* createByType (GLRenderer* renderer, EditModeType type);
+
+private:
+	GLRenderer* m_renderer;
 };
 
 //
--- a/src/glCompiler.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/glCompiler.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -45,9 +45,7 @@
 	{ GL_STACK_OVERFLOW,				"The operation would have caused an overflow" },
 };
 
-CFGENTRY (String, SelectColorBlend, "#0080FF")
-EXTERN_CFGENTRY (Bool, BlackEdges)
-EXTERN_CFGENTRY (String, BackgroundColor)
+ConfigOption (QString SelectColorBlend = "#0080FF")
 
 static QList<int>		g_warnedColors;
 static const QColor		g_BFCFrontColor (64, 192, 80);
@@ -80,6 +78,7 @@
 // =============================================================================
 //
 GLCompiler::GLCompiler (GLRenderer* renderer) :
+	HierarchyElement (parent),
 	m_renderer (renderer)
 {
 	needMerge();
@@ -168,7 +167,7 @@
 			}
 			else if (poly.color == EdgeColor)
 			{
-				qcol = Luma (QColor (cfg::BackgroundColor)) > 40 ? Qt::black : Qt::white;
+				qcol = Luma (QColor (m_config->backgroundColor)) > 40 ? Qt::black : Qt::white;
 			}
 			else
 			{
@@ -208,7 +207,7 @@
 
 	if (blendAlpha != 0.0)
 	{
-		QColor selcolor (cfg::SelectColorBlend);
+		QColor selcolor (m_config->selectColorBlend);
 		double denom = blendAlpha + 1.0;
 		qcol.setRed ((qcol.red() + (selcolor.red() * blendAlpha)) / denom);
 		qcol.setGreen ((qcol.green() + (selcolor.green() * blendAlpha)) / denom);
--- a/src/glCompiler.h	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/glCompiler.h	Mon Aug 31 04:57:16 2015 +0300
@@ -24,7 +24,7 @@
 
 // =============================================================================
 //
-class GLCompiler : protected QOpenGLFunctions
+class GLCompiler : public HierarchyElement, protected QOpenGLFunctions
 {
 public:
 	struct ObjectVBOInfo
--- a/src/glRenderer.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/glRenderer.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -52,22 +52,22 @@
 	{{  0, -1, 0 }, Z, Y, false,  true, true }, // right
 };
 
-CFGENTRY (String, BackgroundColor,			"#FFFFFF")
-CFGENTRY (String, MainColor,					"#A0A0A0")
-CFGENTRY (Float, MainColorAlpha,				1.0)
-CFGENTRY (Int, LineThickness,				2)
-CFGENTRY (Bool, BFCRedGreenView,			false)
-CFGENTRY (Int, Camera,						EFreeCamera)
-CFGENTRY (Bool, BlackEdges,					false)
-CFGENTRY (Bool, DrawAxes,					false)
-CFGENTRY (Bool, DrawWireframe,				false)
-CFGENTRY (Bool, UseLogoStuds,				false)
-CFGENTRY (Bool, AntiAliasedLines,			true)
-CFGENTRY (Bool, RandomColors,				false)
-CFGENTRY (Bool, HighlightObjectBelowCursor,	true)
-CFGENTRY (Bool, DrawSurfaces,				true)
-CFGENTRY (Bool, DrawEdgeLines,				true)
-CFGENTRY (Bool, DrawConditionalLines,		true)
+ConfigOption (QString BackgroundColor = "#FFFFFF")
+ConfigOption (QString MainColor = "#A0A0A0")
+ConfigOption (float MainColorAlpha = 1.0)
+ConfigOption (int LineThickness = 2)
+ConfigOption (bool BfcRedGreenView = false)
+ConfigOption (int Camera = 6)
+ConfigOption (bool BlackEdges = false)
+ConfigOption (bool DrawAxes = false)
+ConfigOption (bool DrawWireframe = false)
+ConfigOption (bool UseLogoStuds = false)
+ConfigOption (bool AntiAliasedLines = true)
+ConfigOption (bool RandomColors = false)
+ConfigOption (bool HighlightObjectBelowCursor = true)
+ConfigOption (bool DrawSurfaces = true)
+ConfigOption (bool DrawEdgeLines = true)
+ConfigOption (bool DrawConditionalLines = true)
 
 // argh
 const char* g_CameraNames[7] =
@@ -99,10 +99,12 @@
 
 // =============================================================================
 //
-GLRenderer::GLRenderer (QWidget* parent) : QGLWidget (parent)
+GLRenderer::GLRenderer (QWidget* parent) :
+	QGLWidget (parent),
+	HierarchyElement (parent)
 {
 	m_isPicking = false;
-	m_camera = (ECamera) cfg::Camera;
+	m_camera = (ECamera) m_config->camera;
 	m_drawToolTip = false;
 	m_editmode = AbstractEditMode::createByType (this, EditModeType::Select);
 	m_panning = false;
@@ -193,7 +195,7 @@
 	glShadeModel (GL_SMOOTH);
 	glEnable (GL_MULTISAMPLE);
 
-	if (cfg::AntiAliasedLines)
+	if (m_config->antiAliasedLines)
 	{
 		glEnable (GL_LINE_SMOOTH);
 		glEnable (GL_POLYGON_SMOOTH);
@@ -247,7 +249,7 @@
 	initializeOpenGLFunctions();
 #endif
 	setBackground();
-	glLineWidth (cfg::LineThickness);
+	glLineWidth (m_config->lineThickness);
 	glLineStipple (1, 0x6666);
 	setAutoFillBackground (false);
 	setMouseTracking (true);
@@ -294,12 +296,12 @@
 //
 QColor GLRenderer::getMainColor()
 {
-	QColor col (cfg::MainColor);
+	QColor col (m_config->mainColor);
 
 	if (not col.isValid())
 		return QColor (0, 0, 0);
 
-	col.setAlpha (cfg::MainColorAlpha * 255.f);
+	col.setAlpha (m_config->mainColorAlpha * 255.f);
 	return col;
 }
 
@@ -313,7 +315,7 @@
 		return;
 	}
 
-	QColor col (cfg::BackgroundColor);
+	QColor col (m_config->backgroundColor);
 
 	if (not col.isValid())
 		return;
@@ -375,7 +377,7 @@
 		zoomAllToFit();
 	}
 
-	if (cfg::DrawWireframe and not isPicking())
+	if (m_config->drawWireframe and not isPicking())
 		glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
 
 	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@@ -429,7 +431,7 @@
 	}
 	else
 	{
-		if (cfg::BFCRedGreenView)
+		if (m_config->bfcRedGreenView)
 		{
 			glEnable (GL_CULL_FACE);
 			glCullFace (GL_BACK);
@@ -442,16 +444,9 @@
 		}
 		else
 		{
-			if (cfg::RandomColors)
-			{
-				drawVBOs (VBOSF_Triangles, VBOCM_RandomColors, GL_TRIANGLES);
-				drawVBOs (VBOSF_Quads, VBOCM_RandomColors, GL_QUADS);
-			}
-			else
-			{
-				drawVBOs (VBOSF_Triangles, VBOCM_NormalColors, GL_TRIANGLES);
-				drawVBOs (VBOSF_Quads, VBOCM_NormalColors, GL_QUADS);
-			}
+			EVBOComplement colors = (m_config->randomColors) ? VBOCM_RandomColors : VBOCM_NormalColors;
+			drawVBOs (VBOSF_Triangles, colors, GL_TRIANGLES);
+			drawVBOs (VBOSF_Quads, colors, GL_QUADS);
 		}
 
 		drawVBOs (VBOSF_Lines, VBOCM_NormalColors, GL_LINES);
@@ -459,7 +454,7 @@
 		drawVBOs (VBOSF_CondLines, VBOCM_NormalColors, GL_LINES);
 		glDisable (GL_LINE_STIPPLE);
 
-		if (cfg::DrawAxes)
+		if (m_config->drawAxes)
 		{
 			glBindBuffer (GL_ARRAY_BUFFER, m_axesVBO);
 			glVertexPointer (3, GL_FLOAT, 0, NULL);
@@ -485,9 +480,9 @@
 void GLRenderer::drawVBOs (EVBOSurface surface, EVBOComplement colors, GLenum type)
 {
 	// Filter this through some configuration options
-	if ((isOneOf (surface, VBOSF_Quads, VBOSF_Triangles) and cfg::DrawSurfaces == false) or
-		(surface == VBOSF_Lines and cfg::DrawEdgeLines == false) or
-		(surface == VBOSF_CondLines and cfg::DrawConditionalLines == false))
+	if ((isOneOf (surface, VBOSF_Quads, VBOSF_Triangles) and m_config->drawSurfaces == false) or
+		(surface == VBOSF_Lines and m_config->drawEdgeLines == false) or
+		(surface == VBOSF_CondLines and m_config->drawConditionalLines == false))
 	{
 		return;
 	}
@@ -888,7 +883,7 @@
 //
 void GLRenderer::contextMenuEvent (QContextMenuEvent* ev)
 {
-	g_win->spawnContextMenu (ev->globalPos());
+	m_window->spawnContextMenu (ev->globalPos());
 }
 
 // =============================================================================
@@ -900,8 +895,8 @@
 		return;
 
 	m_camera = cam;
-	cfg::Camera = (int) cam;
-	g_win->updateEditModeActions();
+	m_config->camera = (int) cam;
+	m_window->updateEditModeActions();
 }
 
 // =============================================================================
@@ -996,7 +991,7 @@
 	delete[] pixeldata;
 
 	// Update everything now.
-	g_win->updateSelection();
+	m_window->updateSelection();
 
 	// Recompile the objects now to update their color
 	for (LDObject* obj : Selection())
@@ -1039,7 +1034,7 @@
 	if (camera() == EFreeCamera and not m_editmode->allowFreeCamera())
 		setCamera (ETopCamera);
 
-	g_win->updateEditModeActions();
+	m_window->updateEditModeActions();
 	update();
 }
 
@@ -1082,14 +1077,14 @@
 		glDisable (GL_DITHER);
 
 		// Use particularly thick lines while picking ease up selecting lines.
-		glLineWidth (qMax<double> (cfg::LineThickness, 6.5));
+		glLineWidth (qMax<double> (m_config->lineThickness, 6.5));
 	}
 	else
 	{
 		glEnable (GL_DITHER);
 
 		// Restore line thickness
-		glLineWidth (cfg::LineThickness);
+		glLineWidth (m_config->lineThickness);
 	}
 }
 
@@ -1526,22 +1521,22 @@
 		}
 	}
 
-	if (g_win->R() == this)
-		g_win->refresh();
+	if (m_window->R() == this)
+		m_window->refresh();
 }
 
 // =============================================================================
 //
 void GLRenderer::highlightCursorObject()
 {
-	if (not cfg::HighlightObjectBelowCursor and objectAtCursor() == null)
+	if (not m_config->highlightObjectBelowCursor and objectAtCursor() == null)
 		return;
 
 	LDObject* newObject = nullptr;
 	LDObject* oldObject = objectAtCursor();
 	qint32 newIndex;
 
-	if (isCameraMoving() or not cfg::HighlightObjectBelowCursor)
+	if (isCameraMoving() or not m_config->highlightObjectBelowCursor)
 	{
 		newIndex = 0;
 	}
@@ -1575,24 +1570,24 @@
 
 void GLRenderer::dragEnterEvent (QDragEnterEvent* ev)
 {
-	if (g_win != null and ev->source() == g_win->getPrimitivesTree() and g_win->getPrimitivesTree()->currentItem() != null)
+	if (m_window != null and ev->source() == m_window->getPrimitivesTree() and m_window->getPrimitivesTree()->currentItem() != null)
 		ev->acceptProposedAction();
 }
 
 void GLRenderer::dropEvent (QDropEvent* ev)
 {
-	if (g_win != null and ev->source() == g_win->getPrimitivesTree())
+	if (m_window != null and ev->source() == m_window->getPrimitivesTree())
 	{
-		QString primName = static_cast<SubfileListItem*> (g_win->getPrimitivesTree()->currentItem())->primitive()->name;
+		QString primName = static_cast<SubfileListItem*> (m_window->getPrimitivesTree()->currentItem())->primitive()->name;
 		LDSubfile* ref = LDSpawn<LDSubfile>();
 		ref->setColor (MainColor);
 		ref->setFileInfo (GetDocument (primName));
 		ref->setPosition (Origin);
 		ref->setTransform (IdentityMatrix);
-		LDDocument::current()->insertObj (g_win->getInsertionPoint(), ref);
+		LDDocument::current()->insertObj (m_window->getInsertionPoint(), ref);
 		ref->select();
-		g_win->buildObjList();
-		g_win->R()->refresh();
+		m_window->buildObjList();
+		m_window->R()->refresh();
 		ev->acceptProposedAction();
 	}
 }
--- a/src/glRenderer.h	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/glRenderer.h	Mon Aug 31 04:57:16 2015 +0300
@@ -135,7 +135,7 @@
 // and selection picking. The instance of GLRenderer is accessible as
 // g_win->R()
 //
-class GLRenderer : public QGLWidget, protected QOpenGLFunctions
+class GLRenderer : public QGLWidget, protected QOpenGLFunctions, public HierarchyElement
 {
 public:
 	Q_OBJECT
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hierarchyelement.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <QMetaObject>
+#include "hierarchyelement.h"
+#include "mainwindow.h"
+
+HierarchyElement::HierarchyElement (QObject* parent) :
+	m_window (nullptr),
+	m_config (nullptr)
+{
+	if (parent)
+	{
+		while (parent->parent() != nullptr)
+			parent = parent->parent();
+	
+		m_window = qobject_cast<MainWindow*> (parent);
+	}
+
+	if (m_window == nullptr)
+	{
+		fprintf (stderr, "FATAL ERROR: Hierarchy element instance %p should be in the hierarchy of a "
+			"MainWindow but isn't.", this);
+		abort();
+	}
+	else
+	{
+		m_config = m_window->configBag();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hierarchyelement.h	Mon Aug 31 04:57:16 2015 +0300
@@ -0,0 +1,19 @@
+#pragma once
+#include <QObject>
+
+class MainWindow;
+class ConfigurationValueBag;
+
+//
+// Objects that are to take part in the MainWindow's hierarchy multiple-inherit from this class to get a few useful
+// pointer members.
+//
+class HierarchyElement
+{
+public:
+	HierarchyElement (QObject* parent);
+
+protected:
+	MainWindow* m_window;
+	ConfigurationValueBag* m_config;
+};
--- a/src/ldDocument.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/ldDocument.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -33,11 +33,8 @@
 #include "ldpaths.h"
 #include "dialogs/openprogressdialog.h"
 
-CFGENTRY (List, RecentFiles, {})
-CFGENTRY (Bool, TryDownloadMissingFiles, false)
-EXTERN_CFGENTRY (String, DownloadFilePath)
-EXTERN_CFGENTRY (Bool, UseLogoStuds)
-EXTERN_CFGENTRY (String, LDrawPath)
+ConfigOption (QStringList RecentFiles)
+ConfigOption (bool TryDownloadMissingFiles = false)
 
 static bool g_loadingMainFile = false;
 static const int g_maxRecentFiles = 10;
@@ -226,7 +223,7 @@
 		return relpath;
 
 	// Try with just the LDraw path first
-	fullPath = format ("%1" DIRSLASH "%2", cfg::LDrawPath, relpath);
+	fullPath = format ("%1" DIRSLASH "%2", g_win->configBag()->lDrawPath, relpath);
 
 	if (QFile::exists (fullPath))
 		return fullPath;
@@ -235,9 +232,10 @@
 	{
 		// Look in sub-directories: parts and p. Also look in net_downloadpath, since that's
 		// where we download parts from the PT to.
-		for (const QString& topdir : QList<QString> ({ cfg::LDrawPath, cfg::DownloadFilePath }))
+		QStringList dirs = { g_win->configBag()->lDrawPath, g_win->configBag()->downloadFilePath };
+		for (const QString& topdir : dirs)
 		{
-			for (const QString& subdir : QList<QString> ({ "parts", "p" }))
+			for (const QString& subdir : QStringList ({ "parts", "p" }))
 			{
 				fullPath = format ("%1" DIRSLASH "%2" DIRSLASH "%3", topdir, subdir, relpath);
 
@@ -574,24 +572,24 @@
 //
 void AddRecentFile (QString path)
 {
-	int idx = cfg::RecentFiles.indexOf (path);
+	QStringList& recentFiles = g_win->configBag()->recentFiles;
+	int idx = recentFiles.indexOf (path);
 
 	// If this file already is in the list, pop it out.
 	if (idx != -1)
 	{
-		if (idx == cfg::RecentFiles.size() - 1)
+		if (idx == recentFiles.size() - 1)
 			return; // first recent file - abort and do nothing
 
-		cfg::RecentFiles.removeAt (idx);
+		recentFiles.removeAt (idx);
 	}
 
 	// If there's too many recent files, drop one out.
-	while (cfg::RecentFiles.size() > (g_maxRecentFiles - 1))
-		cfg::RecentFiles.removeAt (0);
+	while (recentFiles.size() > (g_maxRecentFiles - 1))
+		recentFiles.removeAt (0);
 
 	// Add the file
-	cfg::RecentFiles << path;
-
+	recentFiles << path;
 	Config::Save();
 	g_win->updateRecentFilesMenu();
 }
@@ -671,7 +669,7 @@
 		unknowns << static_cast<LDError*> (obj)->fileReferenced();
 	}
 
-	if (cfg::TryDownloadMissingFiles and not unknowns.isEmpty())
+	if (g_win->configBag()->tryDownloadMissingFiles and not unknowns.isEmpty())
 	{
 		PartDownloader dl;
 
@@ -1270,7 +1268,7 @@
 	// Possibly substitute with logoed studs:
 	// stud.dat -> stud-logo.dat
 	// stud2.dat -> stud-logo2.dat
-	if (cfg::UseLogoStuds and renderinline)
+	if (g_win->configBag()->useLogoStuds and renderinline)
 	{
 		// Ensure logoed studs are loaded first
 		LoadLogoStuds();
--- a/src/ldObject.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/ldObject.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -27,9 +27,9 @@
 #include "colors.h"
 #include "glCompiler.h"
 
-CFGENTRY (String, DefaultName, "")
-CFGENTRY (String, DefaultUser, "")
-CFGENTRY (Bool, UseCALicense, true)
+ConfigOption (QString DefaultName = "")
+ConfigOption (QString DefaultUser = "")
+ConfigOption (bool UseCaLicense = true)
 
 // List of all LDObjects
 QMap<int32, LDObject*> g_allObjects;
@@ -859,7 +859,7 @@
 //
 QString PreferredLicenseText()
 {
-	return (cfg::UseCALicense ? CALicenseText : "");
+	return (g_win->configBag()->useCaLicense ? CALicenseText : "");
 }
 
 // =============================================================================
--- a/src/ldpaths.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/ldpaths.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -1,24 +1,28 @@
 #include <QDir>
 #include "ldpaths.h"
+#include "mainwindow.h"
 #include "dialogs/ldrawpathdialog.h"
 
-CFGENTRY (String, LDrawPath, "")
+ConfigOption (QString LDrawPath)
 
 LDPaths::LDPaths (QObject* parent) :
 	QObject (parent),
+	HierarchyElement (parent),
 	m_dialog (nullptr) {}
 
 void LDPaths::checkPaths()
 {
-	if (not configurePaths (cfg::LDrawPath))
+	QString& pathconfig = m_config->lDrawPath;
+
+	if (not configurePaths (pathconfig))
 	{
-		m_dialog = new LDrawPathDialog (cfg::LDrawPath, false);
+		m_dialog = new LDrawPathDialog (pathconfig, false);
 		connect (m_dialog, SIGNAL (pathChanged(QString)), this, SLOT (configurePaths (QString)));
 
 		if (not m_dialog->exec())
 			Exit();
 		else
-			cfg::LDrawPath = m_dialog->path();
+			pathconfig = m_dialog->path();
 	}
 }
 
--- a/src/ldpaths.h	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/ldpaths.h	Mon Aug 31 04:57:16 2015 +0300
@@ -2,13 +2,14 @@
 #include "main.h"
 
 class QDir;
+class MainWindow;
 
-class LDPaths : public QObject
+class LDPaths : public QObject, public HierarchyElement
 {
 	Q_OBJECT
 
 public:
-	LDPaths (QObject* parent = nullptr);
+	LDPaths (QObject* parent);
 	void checkPaths();
 	bool isValid (const class QDir& path) const;
 
--- a/src/macros.h	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/macros.h	Mon Aug 31 04:57:16 2015 +0300
@@ -84,3 +84,5 @@
 		++FOR_ENUM_NAME (__LINE__)) \
 	for (ENUM NAME = ENUM (FOR_ENUM_NAME (__LINE__)); NAME != ENUM::NumValues; \
 		NAME = ENUM::NumValues)
+
+#define ConfigOption(...)
--- a/src/main.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/main.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -42,7 +42,7 @@
 const Vertex Origin (0.0f, 0.0f, 0.0f);
 const Matrix IdentityMatrix ({1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f});
 
-CFGENTRY (Bool, FirstStart, true)
+ConfigOption (bool firstStart = true)
 
 // =============================================================================
 //
@@ -64,22 +64,22 @@
 		else
 			Critical ("Failed to create configuration file!\n");
 	}
-
-	LDPaths* paths = new LDPaths;
+	
+	MainWindow* win = new MainWindow;
+	LDPaths* paths = new LDPaths (win);
 	paths->checkPaths();
 	paths->deleteLater();
 	InitColors();
 	LoadPrimitives();
-	MainWindow* win = new MainWindow;
 	newFile();
 	win->show();
 
 	// If this is the first start, get the user to configuration. Especially point
 	// them to the profile tab, it's the most important form to fill in.
-	if (cfg::FirstStart)
+	if (win->configBag()->firstStart)
 	{
 		(new ConfigDialog (ConfigDialog::ProfileTab))->exec();
-		cfg::FirstStart = false;
+		win->configBag()->firstStart = false;
 		Config::Save();
 	}
 
--- a/src/main.h	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/main.h	Mon Aug 31 04:57:16 2015 +0300
@@ -31,6 +31,7 @@
 #include "version.h"
 #include "configuration.h"
 #include "format.h"
+#include "hierarchyelement.h"
 
 // Null pointer
 static const std::nullptr_t null = nullptr;
--- a/src/mainwindow.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/mainwindow.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -51,32 +51,23 @@
 #include "ui_mainwindow.h"
 #include "primitives.h"
 #include "editmodes/abstractEditMode.h"
+#include "toolsets/extprogramtoolset.h"
 #include "toolsets/toolset.h"
 
 static bool g_isSelectionLocked = false;
 static QMap<QAction*, QKeySequence> g_defaultShortcuts;
 
-CFGENTRY (Bool, ColorizeObjectsList, true)
-CFGENTRY (String, QuickColorToolbar, "4:25:14:27:2:3:11:1:22:|:0:72:71:15")
-CFGENTRY (Bool, ListImplicitFiles, false)
-CFGENTRY (List, HiddenToolbars, {})
-EXTERN_CFGENTRY (List, RecentFiles)
-EXTERN_CFGENTRY (Bool, DrawAxes)
-EXTERN_CFGENTRY (String, MainColor)
-EXTERN_CFGENTRY (Float, MainColorAlpha)
-EXTERN_CFGENTRY (Bool, DrawWireframe)
-EXTERN_CFGENTRY (Bool, BFCRedGreenView)
-EXTERN_CFGENTRY (Bool, DrawAngles)
-EXTERN_CFGENTRY (Bool, RandomColors)
-EXTERN_CFGENTRY (Bool, DrawSurfaces)
-EXTERN_CFGENTRY (Bool, DrawEdgeLines)
-EXTERN_CFGENTRY (Bool, DrawConditionalLines)
+ConfigOption (bool colorizeObjectsList = true)
+ConfigOption (QString quickColorToolbar = "4:25:14:27:2:3:11:1:22:|:0:72:71:15")
+ConfigOption (bool listImplicitFiles = false)
+ConfigOption (QStringList hiddenToolbars)
 
 // =============================================================================
 //
 MainWindow::MainWindow (QWidget* parent, Qt::WindowFlags flags) :
 	QMainWindow (parent, flags),
-	ui (*new Ui_MainWindow)
+	ui (*new Ui_MainWindow),
+	m_externalPrograms (nullptr)
 {
 	g_win = this;
 	ui.setupUi (this);
@@ -142,6 +133,9 @@
 	{
 		const QMetaObject* meta = toolset->metaObject();
 
+		if (qobject_cast<ExtProgramToolset*> (meta))
+			m_externalPrograms = meta;
+
 		for (int i = 0; i < meta->methodCount(); ++i)
 		{
 			ToolInfo info;
@@ -162,7 +156,7 @@
 		}
 	}
 
-	for (QVariant const& toolbarname : cfg::HiddenToolbars)
+	for (QVariant const& toolbarname : m_configOptions.hiddenToolbars)
 	{
 		QToolBar* toolbar = findChild<QToolBar*> (toolbarname.toString());
 
@@ -231,7 +225,7 @@
 
 	QAction* first = null;
 
-	for (const QVariant& it : cfg::RecentFiles)
+	for (const QVariant& it : m_configOptions.recentFiles)
 	{
 		QString file = it.toString();
 		QAction* recent = new QAction (GetIcon ("open-recent"), file, this);
@@ -249,7 +243,7 @@
 {
 	QList<LDQuickColor> colors;
 
-	for (QString colorname : cfg::QuickColorToolbar.split (":"))
+	for (QString colorname : m_configOptions.quickColorToolbar.split (":"))
 	{
 		if (colorname == "|")
 			colors << LDQuickColor::getSeparator();
@@ -303,9 +297,9 @@
 void MainWindow::updateGridToolBar()
 {
 	// Ensure that the current grid - and only the current grid - is selected.
-	ui.actionGridCoarse->setChecked (cfg::Grid == Grid::Coarse);
-	ui.actionGridMedium->setChecked (cfg::Grid == Grid::Medium);
-	ui.actionGridFine->setChecked (cfg::Grid == Grid::Fine);
+	ui.actionGridCoarse->setChecked (m_configOptions.grid == Grid::Coarse);
+	ui.actionGridMedium->setChecked (m_configOptions.grid == Grid::Medium);
+	ui.actionGridFine->setChecked (m_configOptions.grid == Grid::Fine);
 }
 
 // =============================================================================
@@ -471,8 +465,11 @@
 			item->setBackground (QColor ("#AA0000"));
 			item->setForeground (QColor ("#FFAA00"));
 		}
-		else if (cfg::ColorizeObjectsList and obj->isColored() and
-			obj->color().isValid() and obj->color() != MainColor and obj->color() != EdgeColor)
+		else if (m_configOptions.colorizeObjectsList
+			and obj->isColored()
+			and obj->color().isValid()
+			and obj->color() != MainColor
+			and obj->color() != EdgeColor)
 		{
 			// If the object isn't in the main or edge color, draw this list entry in that color.
 			item->setForeground (obj->color().faceColor());
@@ -681,12 +678,12 @@
 	}
 
 	// Save the toolbar set
-	cfg::HiddenToolbars.clear();
+	m_configOptions.hiddenToolbars.clear();
 
 	for (QToolBar* toolbar : findChildren<QToolBar*>())
 	{
 		if (toolbar->isHidden())
-			cfg::HiddenToolbars << toolbar->objectName();
+			m_configOptions.hiddenToolbars << toolbar->objectName();
 	}
 
 	// Save the configuration before leaving.
@@ -915,8 +912,8 @@
 	if (colinfo == MainColor)
 	{
 		// Use the user preferences for main color here
-		col = cfg::MainColor;
-		col.setAlphaF (cfg::MainColorAlpha);
+		col = g_win->configBag()->mainColor;
+		col.setAlphaF (g_win->configBag()->mainColorAlpha);
 	}
 
 	// Paint the icon border
@@ -1068,14 +1065,14 @@
 		ui.actionRedo->setEnabled (pos < (long) his->getSize() - 1);
 	}
 
-	ui.actionWireframe->setChecked (cfg::DrawWireframe);
-	ui.actionAxes->setChecked (cfg::DrawAxes);
-	ui.actionBfcView->setChecked (cfg::BFCRedGreenView);
-	ui.actionRandomColors->setChecked (cfg::RandomColors);
-	ui.actionDrawAngles->setChecked (cfg::DrawAngles);
-	ui.actionDrawSurfaces->setChecked (cfg::DrawSurfaces);
-	ui.actionDrawEdgeLines->setChecked (cfg::DrawEdgeLines);
-	ui.actionDrawConditionalLines->setChecked (cfg::DrawConditionalLines);
+	ui.actionWireframe->setChecked (m_configOptions.drawWireframe);
+	ui.actionAxes->setChecked (m_configOptions.drawAxes);
+	ui.actionBfcView->setChecked (m_configOptions.bfcRedGreenView);
+	ui.actionRandomColors->setChecked (m_configOptions.randomColors);
+	ui.actionDrawAngles->setChecked (m_configOptions.drawAngles);
+	ui.actionDrawSurfaces->setChecked (m_configOptions.drawSurfaces);
+	ui.actionDrawEdgeLines->setChecked (m_configOptions.drawEdgeLines);
+	ui.actionDrawConditionalLines->setChecked (m_configOptions.drawConditionalLines);
 }
 
 // =============================================================================
@@ -1188,6 +1185,35 @@
 	ui.ringToolSegmentsLabel->setText (format ("%1 / %2", numerator, denominator));
 }
 
+
+//
+// Where is the configuration file located at?
+//
+QString MainWindow::configFilePath (QString file)
+{
+	return Config::DirectoryPath() + DIRSLASH + file;
+}
+
+//
+// Directory of the configuration file.
+//
+QString Config::DirectoryPath()
+{
+	QSettings* settings = SettingsObject();
+	QString result = Dirname (settings->fileName());
+	delete settings;
+	return result;
+}
+
+//
+// Accessor to the settings object
+//
+QSettings* Config::SettingsObject()
+{
+	QString path = qApp->applicationDirPath() + "/" UNIXNAME ".ini";
+	return new QSettings (path, QSettings::IniFormat);
+}
+
 // =============================================================================
 //
 QImage GetImageFromScreencap (uchar* data, int w, int h)
--- a/src/mainwindow.h	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/mainwindow.h	Mon Aug 31 04:57:16 2015 +0300
@@ -27,6 +27,7 @@
 #include "configuration.h"
 #include "ldObject.h"
 #include "colors.h"
+#include "configurationvaluebag.h"
 
 class MessageManager;
 class MainWindow;
@@ -162,6 +163,18 @@
 
 	bool ringToolHiRes() const;
 	int ringToolSegments() const;
+	ConfigurationValueBag* configBag() const { return m_configOptions; }
+
+	template<typename T>
+	auto& config (T key)
+	{
+		return m_configOptions.get (key);
+	}
+
+	class ExtProgramToolset* externalPrograms()
+	{
+		return m_externalPrograms;
+	}
 
 public slots:
 	void updatePrimitives();
@@ -177,6 +190,7 @@
 private:
 	struct ToolInfo { QMetaMethod method; Toolset* object; };
 
+	ConfigurationValueBag m_configOptions;
 	GLRenderer*			m_renderer;
 	LDObjectList		m_sel;
 	QList<LDQuickColor>	m_quickColors;
@@ -188,6 +202,7 @@
 	bool				m_updatingTabs;
 	QVector<Toolset*>	m_toolsets;
 	QMap<QAction*, ToolInfo> m_toolmap;
+	class ExtProgramToolset* m_externalPrograms;
 
 private slots:
 	void slot_selectionChanged();
--- a/src/miscallenous.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/miscallenous.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -85,30 +85,35 @@
 //
 // Grid stuff
 //
-CFGENTRY (Int, Grid, Grid::Medium)
-CFGENTRY (Float, GridCoarseCoordinateSnap, 5.0f)
-CFGENTRY (Float, GridCoarseAngleSnap, 45.0f)
-CFGENTRY (Float, GridMediumCoordinateSnap, 1.0f)
-CFGENTRY (Float, GridMediumAngleSnap, 22.5f)
-CFGENTRY (Float, GridFineCoordinateSnap, 0.1f)
-CFGENTRY (Float, GridFineAngleSnap, 7.5f)
-CFGENTRY (Int, RotationPointType, 0)
-CFGENTRY (Vertex, CustomRotationPoint, Origin)
+ConfigOption (int Grid = 1)
+ConfigOption (float GridCoarseCoordinateSnap = 5.0f)
+ConfigOption (float GridCoarseAngleSnap = 45.0f)
+ConfigOption (float GridMediumCoordinateSnap = 1.0f)
+ConfigOption (float GridMediumAngleSnap = 22.5f)
+ConfigOption (float GridFineCoordinateSnap = 0.1f)
+ConfigOption (float GridFineAngleSnap = 7.5f)
+ConfigOption (int RotationPointType = 0)
+ConfigOption (Vertex CustomRotationPoint = Origin)
 
 const GridData Grids[3] =
 {
-	{ "Coarse",	&cfg::GridCoarseCoordinateSnap,	&cfg::GridCoarseAngleSnap	},
-	{ "Medium",	&cfg::GridMediumCoordinateSnap,	&cfg::GridMediumAngleSnap	},
-	{ "Fine",	&cfg::GridFineCoordinateSnap,	&cfg::GridFineAngleSnap	},
+	{ "Coarse", cfg::GRID_COARSE_COORDINATE_SNAP, cfg::GRID_COARSE_ANGLE_SNAP },
+	{ "Medium", cfg::GRID_MEDIUM_COORDINATE_SNAP, cfg::GRID_MEDIUM_ANGLE_SNAP },
+	{ "Fine", cfg::GRID_FINE_COORDINATE_SNAP, cfg::GRID_FINE_ANGLE_SNAP },
 };
 
+const GridData& CurrentGrid()
+{
+	return Grids[g_win->configBag()->grid];
+}
+
 // =============================================================================
 //
 // Snap the given coordinate value on the current grid's given axis.
 //
 double Grid::Snap (double value, const Grid::Config type)
 {
-	double snapvalue = (type == Coordinate) ? *CurrentGrid().coordinateSnap : *CurrentGrid().angleSnap;
+	double snapvalue = g_win->config ((type == Coordinate) ? CurrentGrid().coordinateSnap : CurrentGrid().angleSnap);
 	double mult = floor (qAbs<double> (value / snapvalue));
 	double out = mult * snapvalue;
 
@@ -153,9 +158,9 @@
 //
 Vertex GetRotationPoint (const LDObjectList& objs)
 {
-	switch (RotationPoint (cfg::RotationPointType))
+	switch (RotationPoint (g_win->configBag()->rotationPointType))
 	{
-		case RotationPoint::ObjectOrigin:
+	case RotationPoint::ObjectOrigin:
 		{
 			LDBoundingBox box;
 
@@ -171,13 +176,14 @@
 			return box.center();
 		}
 
-		case RotationPoint::WorldOrigin:
-			return Origin;
+	case RotationPoint::WorldOrigin:
+		return Origin;
 
-		case RotationPoint::CustomPoint:
-			return cfg::CustomRotationPoint;
+	case RotationPoint::CustomPoint:
+		return g_win->configBag()->customRotationPoint;
 
-		case RotationPoint::NumValues: break;
+	case RotationPoint::NumValues:
+		break;
 	}
 
 	return Vertex();
@@ -191,38 +197,40 @@
 	Ui::RotPointUI ui;
 	ui.setupUi (dlg);
 
-	switch (RotationPoint (cfg::RotationPointType))
+	switch (RotationPoint (g_win->configBag()->rotationPointType))
 	{
-		case RotationPoint::ObjectOrigin:
-			ui.objectPoint->setChecked (true);
-			break;
+	case RotationPoint::ObjectOrigin:
+		ui.objectPoint->setChecked (true);
+		break;
 
-		case RotationPoint::WorldOrigin:
-			ui.worldPoint->setChecked (true);
-			break;
+	case RotationPoint::WorldOrigin:
+		ui.worldPoint->setChecked (true);
+		break;
 
-		case RotationPoint::CustomPoint:
-			ui.customPoint->setChecked (true);
-			break;
+	case RotationPoint::CustomPoint:
+		ui.customPoint->setChecked (true);
+		break;
 
-		case RotationPoint::NumValues: break;
+	case RotationPoint::NumValues:
+		break;
 	}
 
-	ui.customX->setValue (cfg::CustomRotationPoint.x());
-	ui.customY->setValue (cfg::CustomRotationPoint.y());
-	ui.customZ->setValue (cfg::CustomRotationPoint.z());
+	Vertex& custompoint = g_win->configBag()->customRotationPoint;
+	ui.customX->setValue (custompoint.x());
+	ui.customY->setValue (custompoint.y());
+	ui.customZ->setValue (custompoint.z());
 
 	if (not dlg->exec())
 		return;
 
-	cfg::RotationPointType = int (
+	g_win->configBag()->rotationPointType =
 		(ui.objectPoint->isChecked()) ? RotationPoint::ObjectOrigin :
 		(ui.worldPoint->isChecked())  ? RotationPoint::WorldOrigin :
-		RotationPoint::CustomPoint);
+		RotationPoint::CustomPoint;
 
-	cfg::CustomRotationPoint.setX (ui.customX->value());
-	cfg::CustomRotationPoint.setY (ui.customY->value());
-	cfg::CustomRotationPoint.setZ (ui.customZ->value());
+	custompoint.setX (ui.customX->value());
+	custompoint.setY (ui.customY->value());
+	custompoint.setZ (ui.customZ->value());
 }
 
 // =============================================================================
--- a/src/miscallenous.h	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/miscallenous.h	Mon Aug 31 04:57:16 2015 +0300
@@ -22,6 +22,7 @@
 #include "configuration.h"
 #include "main.h"
 #include "basics.h"
+#include "configurationvaluebag.h"
 
 class LDDocument;
 class QColor;
@@ -46,17 +47,12 @@
 struct GridData
 {
 	const char* name;
-	float* coordinateSnap;
-	float* angleSnap;
+	CONFIG_KEY_TYPE(float) coordinateSnap;
+	CONFIG_KEY_TYPE(float) angleSnap;
 };
 
-EXTERN_CFGENTRY (Int, Grid)
 extern const GridData Grids[3];
-
-inline const GridData& CurrentGrid()
-{
-	return Grids[cfg::Grid];
-}
+const GridData& CurrentGrid();
 
 // =============================================================================
 enum class RotationPoint
--- a/src/partDownloader.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/partDownloader.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -31,17 +31,17 @@
 #include "ldDocument.h"
 #include "glRenderer.h"
 
-CFGENTRY (String, DownloadFilePath, "")
-CFGENTRY (Bool, GuessDownloadPaths, true)
-CFGENTRY (Bool, AutoCloseDownloadDialog, true)
+ConfigOption (QString DownloadFilePath)
+ConfigOption (bool GuessDownloadPaths = true)
+ConfigOption (bool AutoCloseDownloadDialog = true)
 
-const QString g_unofficialLibraryURL ("http://ldraw.org/library/unofficial/");
+const char* g_unofficialLibraryURL = "http://ldraw.org/library/unofficial/";
 
 // =============================================================================
 //
 void PartDownloader::staticBegin()
 {
-	PartDownloader dlg;
+	PartDownloader dlg (g_win);
 
 	if (not dlg.checkValidPath())
 		return;
@@ -53,7 +53,7 @@
 //
 QString PartDownloader::getDownloadPath()
 {
-	QString path = cfg::DownloadFilePath;
+	QString path = m_config->downloadFilePath;
 
 	if (DIRSLASH[0] != '/')
 		path.replace (DIRSLASH, "/");
@@ -65,6 +65,7 @@
 //
 PartDownloader::PartDownloader (QWidget* parent) :
 	QDialog (parent),
+	HierarchyElement (parent),
 	m_source (Source (0))
 {
 	setForm (new Ui_DownloadFrom);
@@ -108,7 +109,7 @@
 		if (path.isEmpty())
 			return false;
 
-		cfg::DownloadFilePath = path;
+		m_config->downloadFilePath = path;
 	}
 
 	return true;
@@ -144,7 +145,7 @@
 	dest = dest.simplified();
 
 	// If the user doesn't want us to guess, stop right here.
-	if (not cfg::GuessDownloadPaths)
+	if (not m_config->guessDownloadPaths)
 		return;
 
 	// Ensure .dat extension
@@ -331,7 +332,7 @@
 		for (LDDocument* f : m_files)
 		f->reloadAllSubfiles();
 
-	if (cfg::AutoCloseDownloadDialog and not failed)
+	if (m_config->autoCloseDownloadDialog and not failed)
 	{
 		// Close automatically if desired.
 		accept();
--- a/src/partDownloader.h	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/partDownloader.h	Mon Aug 31 04:57:16 2015 +0300
@@ -33,7 +33,7 @@
 
 // =============================================================================
 //
-class PartDownloader : public QDialog
+class PartDownloader : public QDialog, public HierarchyElement
 {
 public:
 	enum Source
--- a/src/primitives.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/primitives.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -32,10 +32,6 @@
 static PrimitiveScanner* g_activeScanner = null;
 PrimitiveCategory* g_unmatched = null;
 
-EXTERN_CFGENTRY (String, DefaultName)
-EXTERN_CFGENTRY (String, DefaultUser)
-EXTERN_CFGENTRY (Int, DefaultLicense)
-
 static const QStringList g_radialNameRoots =
 {
 	"edge",
@@ -622,10 +618,10 @@
 	QString author = APPNAME;
 	QString license = "";
 
-	if (not cfg::DefaultName.isEmpty())
+	if (not g_win->configBag()->defaultName.isEmpty())
 	{
 		license = PreferredLicenseText();
-		author = format ("%1 [%2]", cfg::DefaultName, cfg::DefaultUser);
+		author = format ("%1 [%2]", g_win->configBag()->defaultName, g_win->configBag()->defaultUser);
 	}
 
 	LDObjectList objs;
--- a/src/toolsets/algorithmtoolset.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/toolsets/algorithmtoolset.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -38,14 +38,9 @@
 #include "ui_addhistoryline.h"
 #include "algorithmtoolset.h"
 
-EXTERN_CFGENTRY (String, DefaultUser)
-
-CFGENTRY (Int, RoundPosition, 3)
-CFGENTRY (Int, RoundMatrix, 4)
-CFGENTRY (Int, SplitLinesSegments, 5)
-EXTERN_CFGENTRY (String, DefaultName)
-EXTERN_CFGENTRY (String, DefaultUser)
-EXTERN_CFGENTRY (Bool, UseCALicense)
+ConfigOption (int roundPositionPrecision = 3)
+ConfigOption (int roundMatrixPrecision = 4)
+ConfigOption (int splitLinesSegments = 5)
 
 AlgorithmToolset::AlgorithmToolset (MainWindow* parent) :
 	Toolset (parent)
@@ -163,9 +158,15 @@
 			Vertex v = mo->position();
 			Matrix t = mo->transform();
 
-			// Note: matrix values are to be rounded to 4 decimals.
-			v.apply ([](Axis, double& a) { RoundToDecimals (a, cfg::RoundPosition); });
-			ApplyToMatrix (t, [](int, double& a) { RoundToDecimals (a, cfg::RoundMatrix); });
+			v.apply ([](Axis, double& a)
+			{
+				RoundToDecimals (a, m_config->roundPositionPrecision);
+			});
+
+			ApplyToMatrix (t, [](int, double& a)
+			{
+				RoundToDecimals (a, m_config->roundMatrixPrecision);
+			});
 
 			mo->setPosition (v);
 			mo->setTransform (t);
@@ -176,7 +177,10 @@
 			for (int i = 0; i < obj->numVertices(); ++i)
 			{
 				Vertex v = obj->vertex (i);
-				v.apply ([](Axis, double& a) { RoundToDecimals (a, cfg::RoundPosition); });
+				v.apply ([](Axis, double& a)
+				{
+					RoundToDecimals (a, m_config->roundPositionPrecision);
+				});
 				obj->setVertex (i, v);
 				num += 3;
 			}
@@ -328,7 +332,7 @@
 	QDialog* dlg = new QDialog;
 	Ui_AddHistoryLine* ui = new Ui_AddHistoryLine;
 	ui->setupUi (dlg);
-	ui->m_username->setText (cfg::DefaultUser);
+	ui->m_username->setText (m_config->defaultUser);
 	ui->m_date->setDate (QDate::currentDate());
 	ui->m_comment->setFocus();
 
@@ -372,13 +376,14 @@
 void AlgorithmToolset::splitLines()
 {
 	bool ok;
-	int segments = QInputDialog::getInt (m_window, APPNAME, "Amount of segments:", cfg::SplitLinesSegments, 0,
+	int segments = QInputDialog::getInt (m_window, APPNAME, "Amount of segments:",
+		m_window->config (m_config->splitLinesSegments), 0,
 		std::numeric_limits<int>::max(), 1, &ok);
 
 	if (not ok)
 		return;
 
-	cfg::SplitLinesSegments = segments;
+	m_config->splitLinesSegments = segments;
 
 	for (LDObject* obj : Selection())
 	{
@@ -539,7 +544,7 @@
 	LDObjectList objs;
 	objs << LDSpawn<LDComment> (subtitle);
 	objs << LDSpawn<LDComment> ("Name: "); // This gets filled in when the subfile is saved
-	objs << LDSpawn<LDComment> (format ("Author: %1 [%2]", cfg::DefaultName, cfg::DefaultUser));
+	objs << LDSpawn<LDComment> (format ("Author: %1 [%2]", m_config->defaultName, m_config->defaultUser));
 	objs << LDSpawn<LDComment> ("!LDRAW_ORG Unofficial_Subpart");
 
 	if (not license.isEmpty())
--- a/src/toolsets/extprogramtoolset.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/toolsets/extprogramtoolset.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -41,68 +41,45 @@
 #include "ui_isecalc.h"
 #include "ui_edger2.h"
 
-enum ExtProgramType
-{
-	Isecalc,
-	Intersector,
-	Coverer,
-	Ytruder,
-	Rectifier,
-	Edger2,
-};
-
 // =============================================================================
 //
-CFGENTRY (String, IsecalcPath, "")
-CFGENTRY (String, IntersectorPath, "")
-CFGENTRY (String, CovererPath, "")
-CFGENTRY (String, YtruderPath, "")
-CFGENTRY (String, RectifierPath, "")
-CFGENTRY (String, Edger2Path, "")
-
-QString* const g_extProgPaths[] =
-{
-	&cfg::IsecalcPath,
-	&cfg::IntersectorPath,
-	&cfg::CovererPath,
-	&cfg::YtruderPath,
-	&cfg::RectifierPath,
-	&cfg::Edger2Path,
-};
-
-CFGENTRY (Bool, IsecalcUsesWine, false)
-CFGENTRY (Bool, IntersectorUsesWine, false)
-CFGENTRY (Bool, CovererUsesWine, false)
-CFGENTRY (Bool, YtruderUsesWine, false)
-CFGENTRY (Bool, RectifierUsesWine, false)
-CFGENTRY (Bool, Edger2UsesWine, false)
-
-bool* const g_extProgWine[] =
-{
-	&cfg::IsecalcUsesWine,
-	&cfg::IntersectorUsesWine,
-	&cfg::CovererUsesWine,
-	&cfg::YtruderUsesWine,
-	&cfg::RectifierUsesWine,
-	&cfg::Edger2UsesWine,
-};
-
-const char* g_extProgNames[] =
-{
-	"Isecalc",
-	"Intersector",
-	"Coverer",
-	"Ytruder",
-	"Rectifier",
-	"Edger2"
-};
+ConfigOption (QString IsecalcPath)
+ConfigOption (QString IntersectorPath)
+ConfigOption (QString CovererPath)
+ConfigOption (QString RectifierPath)
+ConfigOption (QString YtruderPath)
+ConfigOption (QString Edger2Path)
+ConfigOption (bool IsecalcUsesWine = false)
+ConfigOption (bool IntersectorUsesWine = false)
+ConfigOption (bool CovererUsesWine = false)
+ConfigOption (bool YtruderUsesWine = false)
+ConfigOption (bool RectifierUsesWine = false)
+ConfigOption (bool Edger2UsesWine = false)
 
 ExtProgramToolset::ExtProgramToolset (MainWindow* parent) :
-	Toolset (parent) {}
+	Toolset (parent)
+{
+	extProgramInfo[Isecalc].name = "Isecalc";
+	extProgramInfo[Isecalc].path = &m_config->isecalcPath;
+	extProgramInfo[Isecalc].wine = &m_config->isecalcUsesWine;
+	extProgramInfo[Intersector].name = "Intersector";
+	extProgramInfo[Intersector].path = &m_config->intersectorPath;
+	extProgramInfo[Intersector].wine = &m_config->intersectorUsesWine;
+	extProgramInfo[Coverer].name = "Coverer";
+	extProgramInfo[Coverer].path = &m_config->covererPath;
+	extProgramInfo[Coverer].wine = &m_config->covererUsesWine;
+	extProgramInfo[Ytruder].name = "Ytruder";
+	extProgramInfo[Ytruder].path = &m_config->ytruderPath;
+	extProgramInfo[Ytruder].wine = &m_config->ytruderUsesWine;
+	extProgramInfo[Rectifier].name = "Rectifier";
+	extProgramInfo[Rectifier].path = &m_config->rectifierPath;
+	extProgramInfo[Rectifier].wine = &m_config->rectifierUsesWine;
+	extProgramInfo[Edger2].name = "Edger2";
+	extProgramInfo[Edger2].path = &m_config->edger2Path;
+	extProgramInfo[Edger2].wine = &m_config->edger2UsesWine;
+}
 
-// =============================================================================
-//
-static bool MakeTempFile (QTemporaryFile& tmp, QString& fname)
+bool ExtProgramToolset::makeTempFile (QTemporaryFile& tmp, QString& fname)
 {
 	if (not tmp.open())
 		return false;
@@ -112,16 +89,38 @@
 	return true;
 }
 
-// =============================================================================
-//
-static bool CheckExtProgramPath (ExtProgramType program)
+bool ExtProgramToolset::programUsesWine (ExtProgramType program)
+{
+#ifndef Q_OS_WIN32
+	return getWineSetting (program);
+#else
+	return false;
+#endif
+}
+
+bool& ExtProgramToolset::getWineSetting (ExtProgramType program)
 {
-	QString& path = *g_extProgPaths[program];
+	return *extProgramInfo[program].wine;
+}
+
+QString ExtProgramToolset::getPathSetting (ExtProgramType program)
+{
+	return *extProgramInfo[program].path;
+}
+
+QString ExtProgramToolset::externalProgramName (ExtProgramType program)
+{
+	return extProgramInfo[program].name;
+}
+
+QString ExtProgramToolset::checkExtProgramPath(ExtProgramType program)
+{
+	QString& path = getPathSetting (program);
 
 	if (not path.isEmpty())
 		return true;
 
-	ExtProgPathPrompt* dlg = new ExtProgPathPrompt (g_extProgNames[program]);
+	ExtProgPathPrompt* dlg = new ExtProgPathPrompt (externalProgramName (program));
 
 	if (dlg->exec() and not dlg->getPath().isEmpty())
 	{
@@ -134,36 +133,28 @@
 
 // =============================================================================
 //
-static QString ProcessExtProgError (ExtProgramType prog, QProcess& proc)
+QString ExtProgramToolset::errorCodeString (ExtProgramType program, QProcess& process)
 {
-	switch (proc.error())
+	switch (process.error())
 	{
 	case QProcess::FailedToStart:
-		{
-			QString winemessage;
+		if (programUsesWine (program))
+			return tr ("Program failed to start, make sure that Wine is installed and check your permissions.");
 
-#ifndef _WIN32
-			if (*g_extProgWine[prog])
-				winemessage = "make sure Wine is installed and ";
-#else
-			Q_UNUSED (prog);
-#endif
-
-			return format ("Program failed to start, %1check your permissions", winemessage);
-		} break;
+		return tr ("Program failed to start, %1check your permissions");
 
 	case QProcess::Crashed:
-		return "Crashed.";
+		return tr ("Crashed.");
 
 	case QProcess::WriteError:
 	case QProcess::ReadError:
-		return "I/O error.";
+		return tr ("I/O error.");
 
 	case QProcess::UnknownError:
-		return "Unknown error";
+		return tr ("Unknown error");
 
 	case QProcess::Timedout:
-		return "Timed out (30 seconds)";
+		return tr ("Timed out (30 seconds)");
 	}
 
 	return "";
@@ -171,7 +162,7 @@
 
 // =============================================================================
 //
-static void WriteObjects (const LDObjectList& objects, QFile& f)
+void ExtProgramToolset::writeObjects (const LDObjectList& objects, QFile& f)
 {
 	for (LDObject* obj : objects)
 	{
@@ -180,7 +171,7 @@
 			LDSubfile* ref = static_cast<LDSubfile*> (obj);
 			LDObjectList objs = ref->inlineContents (true, false);
 
-			WriteObjects (objs, f);
+			writeObjects (objs, f);
 
 			for (LDObject* obj : objs)
 				obj->destroy();
@@ -192,7 +183,7 @@
 
 // =============================================================================
 //
-static void WriteObjects (const LDObjectList& objects, QString fname)
+void ExtProgramToolset::writeObjects (const LDObjectList& objects, QString fname)
 {
 	// Write the input file
 	QFile f (fname);
@@ -203,7 +194,7 @@
 		return;
 	}
 
-	WriteObjects (objects, f);
+	writeObjects (objects, f);
 	f.close();
 
 #ifdef DEBUG
@@ -213,14 +204,14 @@
 
 // =============================================================================
 //
-void WriteSelection (QString fname)
+void ExtProgramToolset::writeSelection (QString fname)
 {
-	WriteObjects (Selection(), fname);
+	writeObjects (Selection(), fname);
 }
 
 // =============================================================================
 //
-void WriteColorGroup (LDColor color, QString fname)
+void ExtProgramToolset::writeColorGroup (LDColor color, QString fname)
 {
 	LDObjectList objects;
 
@@ -232,38 +223,40 @@
 		objects << obj;
 	}
 
-	WriteObjects (objects, fname);
+	writeObjects (objects, fname);
 }
 
 // =============================================================================
 //
-bool RunExtProgram (ExtProgramType prog, QString path, QString argvstr)
+bool ExtProgramToolset::runExtProgram (ExtProgramType program, QString argvstr)
 {
+	QString path = getPathSetting (program);
 	QTemporaryFile input;
 	QStringList argv = argvstr.split (" ", QString::SkipEmptyParts);
 
-#ifndef _WIN32
-	if (*g_extProgWine[prog])
+#ifndef Q_OS_WIN32
+	if (programUsesWine (program))
 	{
 		argv.insert (0, path);
 		path = "wine";
 	}
-#endif // _WIN32
+#endif // Q_OS_WIN32
 
 	print ("Running command: %1 %2\n", path, argv.join (" "));
 
 	if (not input.open())
 		return false;
 
-	QProcess proc;
+	QProcess process;
 
 	// Begin!
-	proc.setStandardInputFile (input.fileName());
-	proc.start (path, argv);
+	process.setStandardInputFile (input.fileName());
+	process.start (path, argv);
 
-	if (not proc.waitForStarted())
+	if (not process.waitForStarted())
 	{
-		Critical (format ("Couldn't start %1: %2\n", g_extProgNames[prog], ProcessExtProgError (prog, proc)));
+		Critical (format ("Couldn't start %1: %2\n", externalProgramName (program),
+			errorCodeString (program, process)));
 		return false;
 	}
 
@@ -271,27 +264,27 @@
 	input.write ("\n");
 
 	// Wait while it runs
-	proc.waitForFinished();
+	process.waitForFinished();
 
 	QString err = "";
 
-	if (proc.exitStatus() != QProcess::NormalExit)
-		err = ProcessExtProgError (prog, proc);
+	if (process.exitStatus() != QProcess::NormalExit)
+		err = errorCodeString (program, process);
 
 	// Check the return code
-	if (proc.exitCode() != 0)
-		err = format ("Program exited abnormally (return code %1).",  proc.exitCode());
+	if (process.exitCode() != 0)
+		err = format ("Program exited abnormally (return code %1).",  process.exitCode());
 
 	if (not err.isEmpty())
 	{
-		Critical (format ("%1 failed: %2\n", g_extProgNames[prog], err));
+		Critical (format ("%1 failed: %2\n", externalProgramName (program), err));
 		QString filename ("externalProgramOutput.txt");
 		QFile file (filename);
 
 		if (file.open (QIODevice::WriteOnly | QIODevice::Text))
 		{
-			file.write (proc.readAllStandardOutput());
-			file.write (proc.readAllStandardError());
+			file.write (process.readAllStandardOutput());
+			file.write (process.readAllStandardError());
 			print ("Wrote output and error logs to %1", QFileInfo (file).absoluteFilePath());
 		}
 		else
@@ -308,11 +301,11 @@
 
 // =============================================================================
 //
-static void InsertOutput (QString fname, bool replace, QList<LDColor> colorsToReplace)
+void ExtProgramToolset::insertOutput (QString fname, bool replace, QList<LDColor> colorsToReplace)
 {
 #ifdef DEBUG
 	QFile::copy (fname, "./debug_lastOutput");
-#endif // RELEASE
+#endif
 
 	// Read the output file
 	QFile f (fname);
@@ -327,10 +320,10 @@
 
 	// If we replace the objects, delete the selection now.
 	if (replace)
-		g_win->deleteSelection();
+		m_window->deleteSelection();
 
 	for (LDColor color : colorsToReplace)
-		g_win->deleteByColor (color);
+		m_window->deleteByColor (color);
 
 	// Insert the new objects
 	CurrentDocument()->clearSelection();
@@ -347,7 +340,7 @@
 		obj->select();
 	}
 
-	g_win->doFullRefresh();
+	m_window->doFullRefresh();
 }
 
 // =============================================================================
@@ -357,7 +350,7 @@
 {
 	setlocale (LC_ALL, "C");
 
-	if (not CheckExtProgramPath (Ytruder))
+	if (not checkExtProgramPath (Ytruder))
 		return;
 
 	QDialog* dlg = new QDialog;
@@ -384,7 +377,7 @@
 	QString inDATName, outDATName;
 
 	// Make temp files for the input and output files
-	if (not MakeTempFile (indat, inDATName) or not MakeTempFile (outdat, outDATName))
+	if (not makeTempFile (indat, inDATName) or not makeTempFile (outdat, outDATName))
 		return;
 
 	// Compose the command-line arguments
@@ -401,7 +394,7 @@
 
 	WriteSelection (inDATName);
 
-	if (not RunExtProgram (Ytruder, cfg::YtruderPath, argv))
+	if (not runExtProgram (Ytruder, argv))
 		return;
 
 	InsertOutput (outDATName, false, {});
@@ -414,7 +407,7 @@
 {
 	setlocale (LC_ALL, "C");
 
-	if (not CheckExtProgramPath (Rectifier))
+	if (not checkExtProgramPath (Rectifier))
 		return;
 
 	QDialog* dlg = new QDialog;
@@ -428,7 +421,7 @@
 	QString inDATName, outDATName;
 
 	// Make temp files for the input and output files
-	if (not MakeTempFile (indat, inDATName) or not MakeTempFile (outdat, outDATName))
+	if (not makeTempFile (indat, inDATName) or not makeTempFile (outdat, outDATName))
 		return;
 
 	// Compose arguments
@@ -446,7 +439,7 @@
 
 	WriteSelection (inDATName);
 
-	if (not RunExtProgram (Rectifier, cfg::RectifierPath, argv))
+	if (not runExtProgram (Rectifier, argv))
 		return;
 
 	InsertOutput (outDATName, true, {});
@@ -459,7 +452,7 @@
 {
 	setlocale (LC_ALL, "C");
 
-	if (not CheckExtProgramPath (Intersector))
+	if (not checkExtProgramPath (Intersector))
 		return;
 
 	QDialog* dlg = new QDialog;
@@ -501,11 +494,11 @@
 	QTemporaryFile indat, cutdat, outdat, outdat2, edgesdat;
 	QString inDATName, cutDATName, outDATName, outDAT2Name, edgesDATName;
 
-	if (not MakeTempFile (indat, inDATName) or
-		not MakeTempFile (cutdat, cutDATName) or
-		not MakeTempFile (outdat, outDATName) or
-		not MakeTempFile (outdat2, outDAT2Name) or
-		not MakeTempFile (edgesdat, edgesDATName))
+	if (not makeTempFile (indat, inDATName) or
+		not makeTempFile (cutdat, cutDATName) or
+		not makeTempFile (outdat, outDATName) or
+		not makeTempFile (outdat2, outDAT2Name) or
+		not makeTempFile (edgesdat, edgesDATName))
 	{
 		return;
 	}
@@ -534,21 +527,22 @@
 		outDAT2Name
 	});
 
-	WriteColorGroup (inCol, inDATName);
-	WriteColorGroup (cutCol, cutDATName);
+	writeColorGroup (inCol, inDATName);
+	writeColorGroup (cutCol, cutDATName);
 
-	if (not RunExtProgram (Intersector, cfg::IntersectorPath, argv_normal))
+	if (not runExtProgram (Intersector, argv_normal))
 		return;
 
-	InsertOutput (outDATName, false, {inCol});
+	insertOutput (outDATName, false, {inCol});
 
-	if (repeatInverse and RunExtProgram (Intersector, cfg::IntersectorPath, argv_inverse))
-		InsertOutput (outDAT2Name, false, {cutCol});
+	if (repeatInverse and runExtProgram (Intersector, argv_inverse))
+		insertOutput (outDAT2Name, false, {cutCol});
 
-	if (ui.cb_edges->isChecked() and CheckExtProgramPath (Isecalc) and
-		RunExtProgram (Isecalc, cfg::IsecalcPath, Join ({inDATName, cutDATName, edgesDATName})))
+	if (ui.cb_edges->isChecked()
+		and checkExtProgramPath (Isecalc)
+		and runExtProgram (Isecalc, Join ({inDATName, cutDATName, edgesDATName})))
 	{
-		InsertOutput (edgesDATName, false, {});
+		insertOutput (edgesDATName, false, {});
 	}
 }
 
@@ -558,7 +552,7 @@
 {
 	setlocale (LC_ALL, "C");
 
-	if (not CheckExtProgramPath (Coverer))
+	if (not checkExtProgramPath (Coverer))
 		return;
 
 	QDialog* dlg = new QDialog;
@@ -589,9 +583,9 @@
 	QTemporaryFile in1dat, in2dat, outdat;
 	QString in1DATName, in2DATName, outDATName;
 
-	if (not MakeTempFile (in1dat, in1DATName) or
-		not MakeTempFile (in2dat, in2DATName) or
-		not MakeTempFile (outdat, outDATName))
+	if (not makeTempFile (in1dat, in1DATName) or
+		not makeTempFile (in2dat, in2DATName) or
+		not makeTempFile (outdat, outDATName))
 	{
 		return;
 	}
@@ -607,13 +601,13 @@
 		outDATName
 	});
 
-	WriteColorGroup (in1Col, in1DATName);
-	WriteColorGroup (in2Col, in2DATName);
+	writeColorGroup (in1Col, in1DATName);
+	writeColorGroup (in2Col, in2DATName);
 
-	if (not RunExtProgram (Coverer, cfg::CovererPath, argv))
+	if (not runExtProgram (Coverer, argv))
 		return;
 
-	InsertOutput (outDATName, false, {});
+	insertOutput (outDATName, false, {});
 }
 
 // =============================================================================
@@ -622,7 +616,7 @@
 {
 	setlocale (LC_ALL, "C");
 
-	if (not CheckExtProgramPath (Isecalc))
+	if (not checkExtProgramPath (Isecalc))
 		return;
 
 	Ui::IsecalcUI ui;
@@ -655,9 +649,9 @@
 	QTemporaryFile in1dat, in2dat, outdat;
 	QString in1DATName, in2DATName, outDATName;
 
-	if (not MakeTempFile (in1dat, in1DATName) or
-		not MakeTempFile (in2dat, in2DATName) or
-		not MakeTempFile (outdat, outDATName))
+	if (not makeTempFile (in1dat, in1DATName) or
+		not makeTempFile (in2dat, in2DATName) or
+		not makeTempFile (outdat, outDATName))
 	{
 		return;
 	}
@@ -669,10 +663,10 @@
 		outDATName
 	});
 
-	WriteColorGroup (in1Col, in1DATName);
-	WriteColorGroup (in2Col, in2DATName);
-	RunExtProgram (Isecalc, cfg::IsecalcPath, argv);
-	InsertOutput (outDATName, false, {});
+	writeColorGroup (in1Col, in1DATName);
+	writeColorGroup (in2Col, in2DATName);
+	runExtProgram (Isecalc, argv);
+	insertOutput (outDATName, false, {});
 }
 
 // =============================================================================
@@ -681,7 +675,7 @@
 {
 	setlocale (LC_ALL, "C");
 
-	if (not CheckExtProgramPath (Edger2))
+	if (not checkExtProgramPath (Edger2))
 		return;
 
 	QDialog* dlg = new QDialog;
@@ -694,7 +688,7 @@
 	QTemporaryFile in, out;
 	QString inName, outName;
 
-	if (not MakeTempFile (in, inName) or not MakeTempFile (out, outName))
+	if (not makeTempFile (in, inName) or not makeTempFile (out, outName))
 		return;
 
 	int unmatched = ui.unmatched->currentIndex();
@@ -716,10 +710,10 @@
 		outName,
 	});
 
-	WriteSelection (inName);
+	writeSelection (inName);
 
-	if (not RunExtProgram (Edger2, cfg::Edger2Path, argv))
+	if (not runExtProgram (Edger2, argv))
 		return;
 
-	InsertOutput (outName, true, {});
+	insertOutput (outName, true, {});
 }
--- a/src/toolsets/extprogramtoolset.h	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/toolsets/extprogramtoolset.h	Mon Aug 31 04:57:16 2015 +0300
@@ -18,6 +18,26 @@
 
 #pragma once
 #include "toolset.h"
+#include "configurationvaluebag.h"
+
+enum ExtProgramType
+{
+	Isecalc,
+	Intersector,
+	Coverer,
+	Ytruder,
+	Rectifier,
+	Edger2,
+
+	NumExternalPrograms,
+};
+
+struct ExtProgramInfo
+{
+	QString name;
+	QString* path;
+	bool* wine;
+};
 
 class ExtProgramToolset : public Toolset
 {
@@ -30,4 +50,21 @@
 	Q_INVOKABLE void isecalc();
 	Q_INVOKABLE void rectifier();
 	Q_INVOKABLE void ytruder();
+
+private:
+	QString externalProgramName (ExtProgramType program);
+	bool programUsesWine (ExtProgramType program);
+	QString checkExtProgramPath (ExtProgramType program);
+	bool makeTempFile (QTemporaryFile& tmp, QString& fname);
+	bool runExtProgram (ExtProgramType prog, QString argvstr);
+	QString errorCodeString (ExtProgramType program, class QProcess& process);
+	void insertOutput (QString fname, bool replace, QList<LDColor> colorsToReplace);
+	void writeColorGroup (LDColor color, QString fname);
+	void writeObjects (const LDObjectList& objects, QFile& f);
+	void writeObjects (const LDObjectList& objects, QString fname);
+	void writeSelection (QString fname);
+	bool& getWineSetting (ExtProgramType program);
+	QString getPathSetting (ExtProgramType program);
+
+	ExtProgramInfo extProgramInfo[NumExternalPrograms];
 };
--- a/src/toolsets/filetoolset.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/toolsets/filetoolset.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -30,8 +30,6 @@
 #include "filetoolset.h"
 #include "ui_makeprim.h"
 
-EXTERN_CFGENTRY (String, LDrawPath)
-
 FileToolset::FileToolset (MainWindow* parent) :
 	Toolset (parent) {}
 
@@ -101,7 +99,7 @@
 
 void FileToolset::setLDrawPath()
 {
-	(new LDrawPathDialog (cfg::LDrawPath, true))->exec();
+	(new LDrawPathDialog (m_config->ldrawPath, true))->exec();
 }
 
 void FileToolset::exit()
--- a/src/toolsets/movetoolset.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/toolsets/movetoolset.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -23,9 +23,7 @@
 #include "movetoolset.h"
 
 MoveToolset::MoveToolset (MainWindow* parent) :
-	Toolset (parent)
-{
-}
+	Toolset (parent) {}
 
 void MoveToolset::moveSelection (bool up)
 {
@@ -46,26 +44,26 @@
 
 void MoveToolset::gridCoarse()
 {
-	cfg::Grid = Grid::Coarse;
+	m_config->grid = Grid::Coarse;
 	m_window->updateGridToolBar();
 }
 
 void MoveToolset::gridMedium()
 {
-	cfg::Grid = Grid::Medium;
+	m_config->grid = Grid::Medium;
 	m_window->updateGridToolBar();
 }
 
 void MoveToolset::gridFine()
 {
-	cfg::Grid = Grid::Fine;
+	m_config->grid = Grid::Fine;
 	m_window->updateGridToolBar();
 }
 
 void MoveToolset::moveObjects (Vertex vect)
 {
 	// Apply the grid values
-	vect *= *CurrentGrid().coordinateSnap;
+	vect *= m_window->config (CurrentGrid().coordinateSnap);
 
 	for (LDObject* obj : Selection())
 		obj->move (vect);
@@ -101,34 +99,39 @@
 	moveObjects ({0, 0, 1});
 }
 
-static double GetRotateActionAngle()
+double MoveToolset::getRotateActionAngle()
 {
-	return (Pi * *CurrentGrid().angleSnap) / 180;
+	return (Pi * m_window->config (CurrentGrid().angleSnap)) / 180;
 }
 
 void MoveToolset::rotateXPos()
 {
-	RotateObjects (1, 0, 0, GetRotateActionAngle(), Selection());
+	RotateObjects (1, 0, 0, getRotateActionAngle(), Selection());
 }
+
 void MoveToolset::rotateYPos()
 {
-	RotateObjects (0, 1, 0, GetRotateActionAngle(), Selection());
+	RotateObjects (0, 1, 0, getRotateActionAngle(), Selection());
 }
+
 void MoveToolset::rotateZPos()
 {
-	RotateObjects (0, 0, 1, GetRotateActionAngle(), Selection());
+	RotateObjects (0, 0, 1, getRotateActionAngle(), Selection());
 }
+
 void MoveToolset::rotateXNeg()
 {
-	RotateObjects (-1, 0, 0, GetRotateActionAngle(), Selection());
+	RotateObjects (-1, 0, 0, getRotateActionAngle(), Selection());
 }
+
 void MoveToolset::rotateYNeg()
 {
-	RotateObjects (0, -1, 0, GetRotateActionAngle(), Selection());
+	RotateObjects (0, -1, 0, getRotateActionAngle(), Selection());
 }
+
 void MoveToolset::rotateZNeg()
 {
-	RotateObjects (0, 0, -1, GetRotateActionAngle(), Selection());
+	RotateObjects (0, 0, -1, getRotateActionAngle(), Selection());
 }
 
 void MoveToolset::configureRotationPoint()
--- a/src/toolsets/movetoolset.h	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/toolsets/movetoolset.h	Mon Aug 31 04:57:16 2015 +0300
@@ -48,4 +48,5 @@
 private:
 	void moveSelection (bool up);
 	void moveObjects (Vertex vect);
+	double getRotateActionAngle();
 };
--- a/src/toolsets/toolset.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/toolsets/toolset.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -27,6 +27,7 @@
 
 Toolset::Toolset (MainWindow* parent) :
 	QObject (parent),
+	HierarchyElement (parent),
 	m_window (parent) {}
 
 QVector<Toolset*> Toolset::createToolsets (MainWindow* parent)
--- a/src/toolsets/toolset.h	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/toolsets/toolset.h	Mon Aug 31 04:57:16 2015 +0300
@@ -22,14 +22,11 @@
 
 class MainWindow;
 
-class Toolset : public QObject
+class Toolset : public QObject, public HierarchyElement
 {
 	Q_OBJECT
 public:
 	Toolset (MainWindow* parent);
 
 	static QVector<Toolset*> createToolsets (MainWindow* parent);
-
-protected:
-	MainWindow* m_window;
 };
--- a/src/toolsets/viewtoolset.cpp	Sun Aug 30 17:20:55 2015 +0300
+++ b/src/toolsets/viewtoolset.cpp	Mon Aug 31 04:57:16 2015 +0300
@@ -28,15 +28,6 @@
 #include "../glCompiler.h"
 #include "viewtoolset.h"
 
-EXTERN_CFGENTRY (Bool, DrawWireframe)
-EXTERN_CFGENTRY (Bool, BFCRedGreenView)
-EXTERN_CFGENTRY (Bool, DrawAngles)
-EXTERN_CFGENTRY (Bool, RandomColors)
-EXTERN_CFGENTRY (Bool, DrawSurfaces)
-EXTERN_CFGENTRY (Bool, DrawEdgeLines)
-EXTERN_CFGENTRY (Bool, DrawConditionalLines)
-EXTERN_CFGENTRY (Bool, DrawAxes)
-
 ViewToolset::ViewToolset (MainWindow *parent) :
 	Toolset (parent) {}
 
@@ -135,7 +126,7 @@
 
 void ViewToolset::axes()
 {
-	cfg::DrawAxes = not cfg::DrawAxes;
+	m_config->drawAxes = not m_config->drawAxes;
 	m_window->updateActions();
 	m_window->R()->update();
 }
@@ -160,7 +151,7 @@
 
 void ViewToolset::wireframe()
 {
-	cfg::DrawWireframe = not cfg::DrawWireframe;
+	m_config->drawWireframe = not m_config->drawWireframe;
 	m_window->R()->refresh();
 }
 
@@ -182,7 +173,7 @@
 
 void ViewToolset::drawAngles()
 {
-	cfg::DrawAngles = not cfg::DrawAngles;
+	m_config->drawAngles = not m_config->drawAngles;
 	m_window->R()->refresh();
 }
 
@@ -251,10 +242,10 @@
 
 void ViewToolset::bfcView()
 {
-	cfg::BFCRedGreenView = not cfg::BFCRedGreenView;
+	m_config->bfcRedGreenView = not m_config->bfcRedGreenView;
 
-	if (cfg::BFCRedGreenView)
-		cfg::RandomColors = false;
+	if (m_config->bfcRedGreenView)
+		m_config->randomColors = false;
 
 	m_window->updateActions();
 	m_window->R()->refresh();
@@ -282,10 +273,10 @@
 
 void ViewToolset::randomColors()
 {
-	cfg::RandomColors = not cfg::RandomColors;
+	m_config->randomColors = not m_config->randomColors;
 
-	if (cfg::RandomColors)
-		cfg::BFCRedGreenView = false;
+	if (m_config->randomColors)
+		m_config->bfcRedGreenView = false;
 
 	m_window->updateActions();
 	m_window->R()->refresh();
@@ -293,18 +284,18 @@
 
 void ViewToolset::drawSurfaces()
 {
-	cfg::DrawSurfaces = not cfg::DrawSurfaces;
+	m_config->drawSurfaces = not m_config->drawSurfaces;
 	m_window->updateActions();
 }
 
 void ViewToolset::drawEdgeLines()
 {
-	cfg::DrawEdgeLines = not cfg::DrawEdgeLines;
+	m_config->drawEdgeLines = not m_config->drawEdgeLines;
 	m_window->updateActions();
 }
 
 void ViewToolset::drawConditionalLines()
 {
-	cfg::DrawConditionalLines = not cfg::DrawConditionalLines;
+	m_config->drawConditionalLines = not m_config->drawConditionalLines;
 	m_window->updateActions();
 }
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/updaterevision.py	Mon Aug 31 04:57:16 2015 +0300
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+# coding: utf-8
+#
+#	Copyright 2015 Teemu Piippo
+#	All rights reserved.
+#
+#	Redistribution and use in source and binary forms, with or without
+#	modification, are permitted provided that the following conditions
+#	are met:
+#
+#	1. Redistributions of source code must retain the above copyright
+#	   notice, this list of conditions and the following disclaimer.
+#	2. Redistributions in binary form must reproduce the above copyright
+#	   notice, this list of conditions and the following disclaimer in the
+#	   documentation and/or other materials provided with the distribution.
+#	3. Neither the name of the copyright holder nor the names of its
+#	   contributors may be used to endorse or promote products derived from
+#	   this software without specific prior written permission.
+#
+#	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+#	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+#	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
+#	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+#	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+#	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+#	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+#	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+#	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+#	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+import argparse
+import outputfile
+
+def main():
+	import subprocess
+	from datetime import datetime
+	parser = argparser.ArgumentParser (description='Writes a header file with Hg commit information')
+	parser.add_argument ('output')
+	args = parser.parse_args()
+	f = OutputFile (args.output)
+	data = subprocess.check_output (['hg', 'log', '-r.', '--template',
+		'{node|short} {branch} {date|hgdate}']).replace ('\n', '').split (' ')
+
+	rev = data[0]
+	branch = data[1]
+	timestamp = int (data[2])
+	date = datetime.utcfromtimestamp (timestamp)
+	datestring = date.strftime ('%y%m%d-%H%M') if date.year >= 2000 else '000000-0000'
+
+	if len(rev) > 7:
+		rev = rev[0:7]
+
+	if subprocess.check_output (['hg', 'id', '-n']).replace ('\n', '')[-1] == '+':
+		rev += '+'
+
+	f.write ('#define HG_NODE "%s"\n' % rev)
+	f.write ('#define HG_BRANCH "%s"\n' % branch)
+	f.write ('#define HG_DATE_VERSION "%s"\n' % datestring)
+	f.write ('#define HG_DATE_STRING "%s"\n' % date.strftime ('%d %b %Y'))
+	f.write ('#define HG_DATE_TIME %d\n' % int (timestamp))
+	if f.save():
+		print '%s updated to %s' % (f.filename, rev)
+
+if __name__ == '__main__':
+	main()
\ No newline at end of file
--- a/updaterevision.py	Sun Aug 30 17:20:55 2015 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-#
-#	Copyright 2014 Teemu Piippo
-#	All rights reserved.
-#
-#	Redistribution and use in source and binary forms, with or without
-#	modification, are permitted provided that the following conditions
-#	are met:
-#
-#	1. Redistributions of source code must retain the above copyright
-#	   notice, this list of conditions and the following disclaimer.
-#	2. Redistributions in binary form must reproduce the above copyright
-#	   notice, this list of conditions and the following disclaimer in the
-#	   documentation and/or other materials provided with the distribution.
-#	3. Neither the name of the copyright holder nor the names of its
-#	   contributors may be used to endorse or promote products derived from
-#	   this software without specific prior written permission.
-#
-#	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-#	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-#	TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-#	PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
-#	OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-#	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-#	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-#	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-#	LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-#	NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-#	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-
-import sys
-import subprocess
-from datetime import datetime
-
-if len (sys.argv) != 2:
-	print 'usage: %s <output>' % sys.argv[0]
-	quit (1)
-
-oldrev = ''
-
-try:
-	with open (sys.argv[1]) as fp:
-		oldrev = fp.readline().replace ('\n', '').replace ('// ', '')
-except IOError:
-    pass
-
-delim='@'*10
-rev, branch, timestampstr, tags = subprocess.check_output (['hg', 'log', '-r.', '--template',
-	delim.join (['{node|short}', '{branch}', '{date|hgdate}', '{tags}'])]).replace ('\n', '').split (delim)
-
-timestamp = int (timestampstr.split(' ')[0])
-date = datetime.utcfromtimestamp (timestamp)
-datestring = date.strftime ('%y%m%d-%H%M') if date.year >= 2000 else '000000-0000'
-
-if len(rev) > 7:
-	rev = rev[0:7]
-
-if subprocess.check_output (['hg', 'id', '-n']).replace ('\n', '')[-1] == '+':
-	rev += '+'
-
-if rev == oldrev:
-	print "%s is up to date at %s" % (sys.argv[1], rev)
-	quit (0)
-
-with open (sys.argv[1], 'w') as fp:
-	fp.write ('// %s\n' % rev)
-	fp.write ('#define HG_NODE "%s"\n' % rev)
-	fp.write ('#define HG_BRANCH "%s"\n' % branch)
-	fp.write ('#define HG_DATE_VERSION "%s"\n' % datestring)
-	fp.write ('#define HG_DATE_STRING "%s"\n' % date.strftime ('%d %b %Y'))
-	fp.write ('#define HG_DATE_TIME %d\n' % int (timestamp))
-
-	if tags and tags != 'tip':
-		fp.write ('#define HG_TAG "%s"\n' % tags.split(' ')[0])
-
-	print '%s updated to %s' % (sys.argv[1], rev)

mercurial