Merge branch 'master' into gl, reworked stuff

Sat, 07 Sep 2013 13:23:09 +0300

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Sat, 07 Sep 2013 13:23:09 +0300
changeset 487
a350c4b25133
parent 443
a70dd25dd4bb (current diff)
parent 486
25747c37c7be (diff)
child 488
0ea49207a4ec

Merge branch 'master' into gl, reworked stuff

Conflicts:
src/gldraw.cpp
src/gldraw.h

src/aboutDialog.cpp file | annotate | diff | comparison | revisions
src/aboutDialog.h file | annotate | diff | comparison | revisions
src/gldata.cpp file | annotate | diff | comparison | revisions
src/gldata.h file | annotate | diff | comparison | revisions
src/gldraw.cpp file | annotate | diff | comparison | revisions
src/gldraw.h file | annotate | diff | comparison | revisions
src/labeledwidget.h file | annotate | diff | comparison | revisions
src/types.h file | annotate | diff | comparison | revisions
--- a/.gitignore	Fri Aug 16 11:05:21 2013 +0300
+++ b/.gitignore	Sat Sep 07 13:23:09 2013 +0300
@@ -4,4 +4,5 @@
 Makefile
 ldforge
 *.dat
-debug_lastOutput
\ No newline at end of file
+debug_lastOutput
+.kdev_include_paths
\ No newline at end of file
--- a/changelog.txt	Fri Aug 16 11:05:21 2013 +0300
+++ b/changelog.txt	Sat Sep 07 13:23:09 2013 +0300
@@ -6,17 +6,34 @@
 		- Added close, close all and save all actions.
 - Added ability to download parts from the Parts Tracker or from a custom-specified URL. If the resulting
 	file contains references to unknown files, LDForge attempts to recursively download all of them.
-- Color icon border now reflects the color's edge color.
-- Changing to draw mode while in free camera now causes the camera to be changed to top.
+- Converted the configuration code to use QSettings, in practice this means the configuration file moved to
+	the registry under Windows and into ~/.config/LDForge under Linux. Unfortunately this means settings get
+	lost during transition from version 0.2 and 0.3.
 - Corrections to the primitive generator:
 	- Fixed: "Hi-Res" was not prepended to the names of 48/ primitives.
 	- Fixed: Checking the Hi-Res option would not allow segment values over 16.
 	- Added support for multiple spaces before the ring number.
+- Changing to draw mode while in free camera now causes the camera to be changed to top.
+- Added config fields for default name/username/license. This data will be automatically filled
+	into forms that require such information.
+- Upon first start the configuration prompt pops up on its own, defaulting on the profile tab. This
+	way the user can fill in the profile data on the first start and get that out of the way (and
+	gives the opportunity to see the other config fields)
+- Added new action "Add History Line" for quickly inserting 0 !HISTORY lines to headers
+- Added new action "Go to line", default shortcut Ctrl-G. It should be obvious what it does.
+- Added support for logoed studs, this should satisfy Steffen. :p
 - Added support for '0 BFC CLIP' and '0 BFC NOCLIP' and added auto-correction from errorneous MLCAD
 	syntax ('0 BFC CERTIFY CLIP').
+- When an external program is attempted to be used without a binary path defined, one will be asked
+	with an input dialog instead of being told to go to configuration to set the path.
+- When adding edges with Intersector (which is done with Isecalc), the user is prompted for Isecalc's
+	path now as well if necessary instead of just ignoring it and not adding the edgelines.
+- BFC red/green view and black edges no longer default to true.
 - If the vertex snapper finds a vertex closer than 4 pixels, it likely is the vertex being looked for
 	and the algorithm can terminate early, hopefully this will save a few cycles on large parts.
 - The camera icons now draw real tooltips instead of emulated ones.
+- Color icon border now reflects the color's edge color.
+- Fixed: The message log was still written with black text with dark backgrounds.
 
 =================================================
 == Changes in version 0.2-alpha
--- a/mkqrc.sh	Fri Aug 16 11:05:21 2013 +0300
+++ b/mkqrc.sh	Sat Sep 07 13:23:09 2013 +0300
@@ -4,7 +4,6 @@
 FILES=$(echo ./icons/*.* data/*.* LICENSE LICENSE.icons)
 
 printf "" > $QRCFILE
-
 printf "<!DOCTYPE RCC>\n<RCC version=\"1.0\">\n<qresource>\n" >> $QRCFILE
 
 for f in $FILES; do
--- a/src/aboutDialog.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-/*
- *  LDForge: LDraw parts authoring CAD
- *  Copyright (C) 2013 Santeri Piippo
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation, either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <QDesktopServices>
-#include <QPushButton>
-#include <QUrl>
-#include "aboutDialog.h"
-#include "ui_about.h"
-#include "gui.h"
-
-AboutDialog::AboutDialog (QWidget* parent, Qt::WindowFlags f) :
-	QDialog (parent, f) {
-	
-	Ui::AboutUI ui;
-	ui.setupUi (this);
-	ui.versionInfo->setText (fmt (tr ("LDForge %1"), fullVersionString()));
-	
-	QPushButton* mailButton = new QPushButton;
-	mailButton->setText ("Contact");
-	mailButton->setIcon (getIcon ("mail"));
-	ui.buttonBox->addButton (static_cast<QAbstractButton*> (mailButton), QDialogButtonBox::HelpRole);
-	connect (ui.buttonBox, SIGNAL (helpRequested()), this, SLOT (slot_mail()));
-	
-	setWindowTitle ("About " APPNAME);
-}
-
-void AboutDialog::slot_mail() {
-	QDesktopServices::openUrl (QUrl ("mailto:Santeri Piippo <arezey@gmail.com>?subject=LDForge"));
-}
-
-#include "build/moc_aboutDialog.cpp"
\ No newline at end of file
--- a/src/aboutDialog.h	Fri Aug 16 11:05:21 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-/*
- *  LDForge: LDraw parts authoring CAD
- *  Copyright (C) 2013 Santeri Piippo
- *
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation, either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef ZZ_ABOUTDIALOG_H
-#define ZZ_ABOUTDIALOG_H
-
-#include <QDialog>
-#include "common.h"
-#include "types.h"
-
-class QPushButton;
-
-class AboutDialog : public QDialog {
-	Q_OBJECT
-	
-public:
-	AboutDialog (QWidget* parent = null, Qt::WindowFlags f = 0);
-	
-private slots:
-	void slot_mail();
-};
-
-#endif // ZZ_ABOUTDIALOG_H
\ No newline at end of file
--- a/src/actions.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/actions.h	Sat Sep 07 13:23:09 2013 +0300
@@ -1,3 +1,21 @@
+/*
+ *  LDForge: LDraw parts authoring CAD
+ *  Copyright (C) 2013 Santeri Piippo
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
 act (New)
 act (NewFile)
 act (Open)
@@ -85,5 +103,7 @@
 act (RotateZNeg)
 act (RotateZPos)
 act (RotationPoint)
+act (AddHistoryLine)
+act (JumpTo)
 
 #undef act
\ No newline at end of file
--- a/src/addObjectDialog.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/addObjectDialog.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -1,30 +1,30 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <qgridlayout.h>
-#include <qcheckbox.h>
-#include <qdialogbuttonbox.h>
-#include <qspinbox.h>
-#include <qlabel.h>
-#include <qlistwidget.h>
-#include <qtreewidget.h>
-#include <qlineedit.h>
-#include <qpushbutton.h>
+#include <QGridLayout>
+#include <QCheckBox>
+#include <QDialogButtonBox>
+#include <QSpinBox>
+#include <QLabel>
+#include <QListWidget>
+#include <QTreeWidget>
+#include <QLineEdit>
+#include <QPushButton>
 #include "gui.h"
 #include "addObjectDialog.h"
 #include "file.h"
@@ -35,6 +35,8 @@
 #include "misc.h"
 #include "primitives.h"
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 class SubfileListItem : public QTreeWidgetItem {
 	PROPERTY (Primitive*, primInfo, setPrimInfo)
 	
@@ -46,8 +48,7 @@
 };
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 AddObjectDialog::AddObjectDialog (const LDObject::Type type, LDObject* obj, QWidget* parent) : QDialog (parent) {
 	setlocale (LC_ALL, "C");
 	
@@ -58,7 +59,7 @@
 	case LDObject::Comment:
 		le_comment = new QLineEdit;
 		if (obj)
-			le_comment->setText (static_cast<LDCommentObject*> (obj)->text);
+			le_comment->setText (static_cast<LDComment*> (obj)->text);
 		
 		le_comment->setMinimumWidth (384);
 		break;
@@ -72,7 +73,7 @@
 		break;
 	
 	case LDObject::Quad:
-	case LDObject::CondLine:
+	case LDObject::CndLine:
 		coordCount = 12;
 		break;
 	
@@ -81,62 +82,60 @@
 		break;
 	
 	case LDObject::BFC:
-		rb_bfcType = new RadioBox ("Statement", {}, 0, Qt::Vertical);
+		rb_bfcType = new RadioGroup ("Statement", {}, 0, Qt::Vertical);
 		
-		for (int i = 0; i < LDBFCObject::NumStatements; ++i) {
+		for (int i = 0; i < LDBFC::NumStatements; ++i) {
 			// Separate these in two columns
-			if (i == LDBFCObject::NumStatements / 2)
+			if (i == LDBFC::NumStatements / 2)
 				rb_bfcType->rowBreak();
 			
-			rb_bfcType->addButton (LDBFCObject::statements[i]);
+			rb_bfcType->addButton (LDBFC::statements[i]);
 		}
 		
 		if (obj)
-			rb_bfcType->setValue ((int) static_cast<LDBFCObject*> (obj)->type);
+			rb_bfcType->setValue ((int) static_cast<LDBFC*> (obj)->type);
 		break;
 	
 	case LDObject::Subfile:
-		{
-			coordCount = 3;
-			
-			// If the primitive lister is busy writing data, we have to wait
-			// for that to happen first. This should be quite considerably rare.
-			while (primitiveLoaderBusy())
-				;
-			
-			tw_subfileList = new QTreeWidget();
-			tw_subfileList->setHeaderLabel (tr ("Primitives"));
+		coordCount = 3;
+		
+		// If the primitive lister is busy writing data, we have to wait
+		// for that to happen first. This should be quite considerably rare.
+		while (primitiveLoaderBusy())
+			;
+		
+		tw_subfileList = new QTreeWidget();
+		tw_subfileList->setHeaderLabel (tr ("Primitives"));
+		
+		for (PrimitiveCategory& cat : g_PrimitiveCategories) {
+			SubfileListItem* parentItem = new SubfileListItem (tw_subfileList, null);
+			parentItem->setText (0, cat.name());
+			QList<QTreeWidgetItem*> subfileItems;
 			
-			for (PrimitiveCategory& cat : g_PrimitiveCategories) {
-				SubfileListItem* parentItem = new SubfileListItem (tw_subfileList, null);
-				parentItem->setText (0, cat.name());
-				QList<QTreeWidgetItem*> subfileItems;
+			for (Primitive& prim : cat.prims) {
+				SubfileListItem* item = new SubfileListItem (parentItem, &prim);
+				item->setText (0, fmt ("%1 - %2", prim.name, prim.title));
+				subfileItems << item;
 				
-				for (Primitive& prim : cat.prims) {
-					SubfileListItem* item = new SubfileListItem (parentItem, &prim);
-					item->setText (0, fmt ("%1 - %2", prim.name, prim.title));
-					subfileItems << item;
-					
-					// If this primitive is the one the current object points to,
-					// select it by default
-					if (obj && static_cast<LDSubfileObject*> (obj)->fileInfo()->name() == prim.name)
-						tw_subfileList->setCurrentItem (item);
-				}
-				
-				tw_subfileList->addTopLevelItem (parentItem);
+				// If this primitive is the one the current object points to,
+				// select it by default
+				if (obj && static_cast<LDSubfile*> (obj)->fileInfo()->name() == prim.name)
+					tw_subfileList->setCurrentItem (item);
 			}
 			
-			connect (tw_subfileList, SIGNAL (itemSelectionChanged()), this, SLOT (slot_subfileTypeChanged()));
-			lb_subfileName = new QLabel ("File:");
-			le_subfileName = new QLineEdit;
-			le_subfileName->setFocus();
-			
-			if (obj) {
-				LDSubfileObject* ref = static_cast<LDSubfileObject*> (obj);
-				le_subfileName->setText (ref->fileInfo()->name());
-			}
-			break;
+			tw_subfileList->addTopLevelItem (parentItem);
 		}
+		
+		connect (tw_subfileList, SIGNAL (itemSelectionChanged()), this, SLOT (slot_subfileTypeChanged()));
+		lb_subfileName = new QLabel ("File:");
+		le_subfileName = new QLineEdit;
+		le_subfileName->setFocus();
+		
+		if (obj) {
+			LDSubfile* ref = static_cast<LDSubfile*> (obj);
+			le_subfileName->setText (ref->fileInfo()->name());
+		}
+		break;
 	
 	default:
 		critical (fmt ("Unhandled LDObject type %1 (%2) in AddObjectDialog", (int) type, typeName));
@@ -154,7 +153,7 @@
 		if (obj != null)
 			colnum = obj->color();
 		else
-			colnum = (type == LDObject::CondLine || type == LDObject::Line) ? edgecolor : maincolor;
+			colnum = (type == LDObject::CndLine || type == LDObject::Line) ? edgecolor : maincolor;
 		
 		pb_color = new QPushButton;
 		setButtonBackground (pb_color, colnum);
@@ -173,7 +172,7 @@
 	
 	switch (type) {
 	case LDObject::Line:
-	case LDObject::CondLine:
+	case LDObject::CndLine:
 	case LDObject::Triangle:
 	case LDObject::Quad:
 		// Apply coordinates
@@ -246,8 +245,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void AddObjectDialog::setButtonBackground (QPushButton* button, short colnum) {
 	LDColor* col = getColor (colnum);
 	
@@ -259,8 +257,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 str AddObjectDialog::currentSubfileName() {
 	SubfileListItem* item = static_cast<SubfileListItem*> (tw_subfileList->currentItem());
 	
@@ -271,16 +268,14 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void AddObjectDialog::slot_colorButtonClicked() {
 	ColorSelector::getColor (colnum, colnum, this);
 	setButtonBackground (pb_color, colnum);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void AddObjectDialog::slot_subfileTypeChanged() {
 	str name = currentSubfileName();
 	
@@ -289,7 +284,8 @@
 }
 
 // =============================================================================
-template<class T> T* initObj (LDObject*& obj) {
+// -----------------------------------------------------------------------------
+template<class T> static T* initObj (LDObject*& obj) {
 	if (obj == null)
 		obj = new T;
 	
@@ -297,8 +293,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void AddObjectDialog::staticDialog (const LDObject::Type type, LDObject* obj) {
 	setlocale (LC_ALL, "C");
 	
@@ -320,7 +315,7 @@
 		return;
 	
 	if (type == LDObject::Subfile) {
-		List<str> matrixstrvals = container_cast<QStringList, List<str>> (str (dlg.le_matrix->text()).split (" "));
+		QStringList matrixstrvals = dlg.le_matrix->text().split (" ");
 		
 		if (matrixstrvals.size() == 9) {
 			double matrixvals[9];
@@ -336,7 +331,7 @@
 	switch (type) {
 	case LDObject::Comment:
 		{
-			LDCommentObject* comm = initObj<LDCommentObject> (obj);
+			LDComment* comm = initObj<LDComment> (obj);
 			comm->text = dlg.le_comment->text();
 		}
 		break;
@@ -344,7 +339,7 @@
 	case LDObject::Line:
 	case LDObject::Triangle:
 	case LDObject::Quad:
-	case LDObject::CondLine:
+	case LDObject::CndLine:
 		if (!obj)
 			obj = LDObject::getDefault (type);
 		
@@ -359,14 +354,14 @@
 	
 	case LDObject::BFC:
 		{
-			LDBFCObject* bfc = initObj<LDBFCObject> (obj);
-			bfc->type = (LDBFCObject::Type) dlg.rb_bfcType->value();
+			LDBFC* bfc = initObj<LDBFC> (obj);
+			bfc->type = (LDBFC::Type) dlg.rb_bfcType->value();
 		}
 		break;
 	
 	case LDObject::Vertex:
 		{
-			LDVertexObject* vert = initObj<LDVertexObject> (obj);
+			LDVertex* vert = initObj<LDVertex> (obj);
 			
 			for (const Axis ax : g_Axes)
 				vert->pos[ax] = dlg.dsb_coords[ax]->value();
@@ -385,7 +380,7 @@
 				return;
 			}
 			
-			LDSubfileObject* ref = initObj<LDSubfileObject> (obj);
+			LDSubfile* ref = initObj<LDSubfile> (obj);
 			
 			for (const Axis ax : g_Axes)
 				ref->setCoordinate (ax, dlg.dsb_coords[ax]->value());
@@ -408,6 +403,4 @@
 	}
 	
 	g_win->fullRefresh();
-}
-
-#include "build/moc_addObjectDialog.cpp"
\ No newline at end of file
+}
\ No newline at end of file
--- a/src/addObjectDialog.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/addObjectDialog.h	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -24,7 +24,7 @@
 
 class QTreeWidgetItem;
 class QLineEdit;
-class RadioBox;
+class RadioGroup;
 class QCheckBox;
 class QSpinBox;
 class QLabel;
@@ -50,7 +50,7 @@
 	QPushButton* pb_color;
 	
 	// BFC-related widgets
-	RadioBox* rb_bfcType;
+	RadioGroup* rb_bfcType;
 	
 	// Subfile stuff
 	QTreeWidget* tw_subfileList;
--- a/src/colorSelectDialog.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/colorSelectDialog.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -14,6 +14,9 @@
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *  =====================================================================
+ *
+ *  colorSelectDialog.cpp: Color selector box.
  */
 
 #include <QGraphicsScene>
@@ -32,47 +35,43 @@
 static const int g_numColumns = 16;
 static const short g_squareSize = 32;
 
-extern_cfg (str, gl_maincolor);
-extern_cfg (float, gl_maincolor_alpha);
+extern_cfg (String, gl_maincolor);
+extern_cfg (Float, gl_maincolor_alpha);
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 ColorSelector::ColorSelector (short defval, QWidget* parent) : QDialog (parent) {
 	// Remove the default color if it's invalid
 	if (!::getColor (defval))
 		defval = -1;
-
+	
 	m_firstResize = true;
 	ui = new Ui_ColorSelUI;
 	ui->setupUi (this);
-
+	
 	m_scene = new QGraphicsScene;
 	ui->viewport->setScene (m_scene);
 	setSelection (::getColor (defval));
-
+	
 	// not really an icon but eh
 	m_scene->setBackgroundBrush (getIcon ("checkerboard"));
-
 	drawScene();
-
+	
 	int width = viewportWidth();
 	ui->viewport->setMinimumWidth (width);
 	ui->viewport->setMaximumWidth (width);
-
+	
 	drawColorInfo();
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 ColorSelector::~ColorSelector() {
 	delete ui;
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ColorSelector::drawScene() {
 	const int numCols = g_numColumns;
 	const int square = g_squareSize;
@@ -119,22 +118,19 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 int ColorSelector::numRows() const {
 	return (MAX_COLORS / g_numColumns);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 int ColorSelector::viewportWidth() const {
 	return g_numColumns * g_squareSize + 21;
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ColorSelector::drawColorInfo() {
 	if (!sel()) {
 		ui->colorLabel->setText ("---");
@@ -145,8 +141,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ColorSelector::resizeEvent (QResizeEvent* ev) {
 	// If this is the first resize, check if we need to scroll down to see the
 	// currently selected color. We cannot do this in the constructor because the
@@ -167,8 +162,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ColorSelector::mousePressEvent (QMouseEvent* event) {
 	QPointF scenepos = ui->viewport->mapToScene (event->pos());
 	
@@ -187,8 +181,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 bool ColorSelector::getColor (short& val, short int defval, QWidget* parent) {
 	ColorSelector dlg (defval, parent);
 	
@@ -198,6 +191,4 @@
 	}
 	
 	return false;
-}
-
-#include "build/moc_colorSelectDialog.cpp"
\ No newline at end of file
+}
\ No newline at end of file
--- a/src/colors.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/colors.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -14,6 +14,11 @@
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *  =====================================================================
+ *
+ *  colors.cpp: LDraw color management. LDConfig.ldr parsing is not here!
+ *  TODO: Make LDColor more full-fledged, add support for direct colors.
+ *  TODO: g_LDColors should probably be a map.
  */
 
 #include "common.h"
@@ -26,20 +31,23 @@
 
 static LDColor* g_LDColors[MAX_COLORS];
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void initColors() {
 	LDColor* col;
 	print ("%1: initializing color information.\n", __func__);
 	
 	// Always make sure there's 16 and 24 available. They're special like that.
 	col = new LDColor;
+	col->faceColor =
 	col->hexcode = "#AAAAAA";
-	col->faceColor = col->hexcode;
 	col->edgeColor = Qt::black;
 	g_LDColors[maincolor] = col;
 	
 	col = new LDColor;
+	col->faceColor =
+	col->edgeColor =
 	col->hexcode = "#000000";
-	col->edgeColor = col->faceColor = Qt::black;
 	g_LDColors[edgecolor] = col;
 	
 	parseLDConfig();
@@ -65,7 +73,8 @@
 }
 
 // =============================================================================
-uchar luma (QColor& col) {
+// -----------------------------------------------------------------------------
+int luma (QColor& col) {
 	return (0.2126f * col.red()) +
 	       (0.7152f * col.green()) +
 	       (0.0722f * col.blue());
--- a/src/colors.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/colors.h	Sat Sep 07 13:23:09 2013 +0300
@@ -19,7 +19,7 @@
 #ifndef COLORS_H
 #define COLORS_H
 
-#include <qcolor.h>
+#include <QColor>
 #include "common.h"
 
 #define MAX_COLORS 512
@@ -32,7 +32,7 @@
 };
 
 void initColors();
-uchar luma (QColor& col);
+int luma (QColor& col);
 
 // Safely gets a color with the given number or null if no such color.
 LDColor* getColor (short colnum);
--- a/src/common.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/common.h	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -56,17 +56,9 @@
 #define RELEASE
 #endif // BUILD_ID
 
-#ifndef RELEASE
-# define devf(...) doDevf (__func__, __VA_ARGS__);
-#else
-# define devf(...)
-#endif // RELEASE
-
 #define alias auto&
 #define elif else if
 
-void doDevf (const char* func, const char* fmtstr, ...);
-
 // Null pointer
 static const std::nullptr_t null = nullptr;
 
--- a/src/config.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/config.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -14,6 +14,12 @@
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *  =====================================================================
+ *
+ *  config.cpp: Configuration management. I don't like how unsafe QSettings
+ *  is so this implements a type-safer and idenitifer-safer wrapping system of
+ *  configuration variables. QSettings is used underlyingly, this is a matter
+ *  of interface.
  */
 
 #include <errno.h>
@@ -26,10 +32,13 @@
 #include "gui.h"
 #include "file.h"
 
-config* g_configPointers[MAX_CONFIG];
+Config* g_configPointers[MAX_CONFIG];
 static ushort g_cfgPointerCursor = 0;
 
 // =============================================================================
+// Get the QSettings object. A portable build refers to a file in the current
+// directory, a non-portable build to ~/.config/LDForge or to registry.
+// -----------------------------------------------------------------------------
 static QSettings* getSettingsObject() {
 #ifdef PORTABLE
 # ifdef _WIN32
@@ -43,17 +52,22 @@
 #endif // PORTABLE
 }
 
+Config::Config (const char* name, const char* defstring) :
+	name (name), m_defstring (defstring) {}
+
 // =============================================================================
 // Load the configuration from file
-bool config::load() {
+// -----------------------------------------------------------------------------
+bool Config::load() {
 	QSettings* settings = getSettingsObject();
-	print ("config::load: Loading configuration file from %1...\n", settings->fileName());
+	print ("config::load: Loading configuration file from %1\n", settings->fileName());
 	
-	for (config* cfg : g_configPointers) {
+	for (Config* cfg : g_configPointers) {
 		if (!cfg)
 			break;
 		
-		cfg->loadFromConfig (settings);
+		QVariant val = settings->value (cfg->name, cfg->defaultVariant());
+		cfg->loadFromVariant (val);
 	}
 	
 	settings->deleteLater();
@@ -61,73 +75,20 @@
 }
 
 // =============================================================================
-void intconfig::loadFromConfig (const QSettings* cfg) {
-	QVariant val = cfg->value (name, str::number (defval));
-	value = val.toInt();
-}
-
-void floatconfig::loadFromConfig (const QSettings* cfg) {
-	QVariant val = cfg->value (name, str::number (defval));
-	value = val.toFloat();
-}
-
-void strconfig::loadFromConfig (const QSettings* cfg) {
-	QVariant val = cfg->value (name, defval);
-	value = val.toString();
-}
-
-void boolconfig::loadFromConfig (const QSettings* cfg) {
-	QVariant val = cfg->value (name, str::number (defval));
-	value = val.toBool();
-}
-
-void keyseqconfig::loadFromConfig (const QSettings* cfg) {
-	QVariant val = cfg->value (name, defval.toString());
-	value = keyseq (val.toString());
-}
-
-// =============================================================================
-// TODO: make virtual
-str config::toString() const {
-	switch (getType()) {
-	case Type_int:
-		return fmt ("%1", static_cast<const intconfig*> (this)->value);
-		break;
-	
-	case Type_str:
-		return static_cast<const strconfig*> (this)->value;
-		break;
-	
-	case Type_float:
-		return fmt ("%1", static_cast<const floatconfig*> (this)->value);
-		break;
-	
-	case Type_bool:
-		return (static_cast<const boolconfig*> (this)->value) ? "true" : "false";
-		break;
-	
-	case Type_keyseq:
-		return static_cast<const keyseqconfig*> (this)->value.toString();
-		break;
-	
-	default:
-		break;
-	}
-	
-	return "";
-}
-
-// =============================================================================
 // Save the configuration to disk
-bool config::save() {
+// -----------------------------------------------------------------------------
+bool Config::save() {
 	QSettings* settings = getSettingsObject();
 	print ("Saving configuration to %1...\n", settings->fileName());
 	
-	for (config* cfg : g_configPointers) {
+	for (Config* cfg : g_configPointers) {
 		if (!cfg)
 			break;
 		
-		settings->setValue (cfg->name, cfg->toString());
+		if (cfg->isDefault())
+			continue;
+		
+		settings->setValue (cfg->name, cfg->toVariant());
 	}
 	
 	settings->sync();
@@ -136,8 +97,10 @@
 }
 
 // =============================================================================
-void config::reset() {
-	for (config* cfg : g_configPointers) {
+// Reset configuration defaults.
+// -----------------------------------------------------------------------------
+void Config::reset() {
+	for (Config* cfg : g_configPointers) {
 		if (!cfg)
 			break;
 		
@@ -146,32 +109,27 @@
 }
 
 // =============================================================================
-str config::filepath (str file) {
-	return config::dirpath() + DIRSLASH + file;
+// Where is the configuration file located at? Note that the Windows build uses
+// the registry so only use this with PORTABLE code.
+// -----------------------------------------------------------------------------
+str Config::filepath (str file) {
+	return Config::dirpath() + DIRSLASH + file;
 }
 
 // =============================================================================
-str config::dirpath() {
+// Directory of the configuration file. PORTABLE code here as well.
+// -----------------------------------------------------------------------------
+str Config::dirpath() {
 	QSettings* cfg = getSettingsObject();
 	return dirname (cfg->fileName());
 }
 
 // =============================================================================
-str config::defaultString() const {
-	str defstring = m_defstring;
-	
-	// String types inevitably get extra quotes in their default string due to
-	// preprocessing stuff. We can only remove them now...
-	if (getType() == Type_str) {
-		defstring.remove (0, 1);
-		defstring.chop (1);
-	}
-	
-	return defstring;
-}
-
-// =============================================================================
-void addConfig (config* ptr) {
+// We cannot just add config objects to a list or vector because that would rely
+// on the vector's c-tor being called before the configs' c-tors. With global
+// variables we cannot assume that!! Therefore we need to use a C-style array here.
+// -----------------------------------------------------------------------------
+void Config::addToArray (Config* ptr) {
 	if (g_cfgPointerCursor == 0)
 		memset (g_configPointers, 0, sizeof g_configPointers);
 	
--- a/src/config.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/config.h	Sat Sep 07 13:23:09 2013 +0300
@@ -23,6 +23,7 @@
 
 // =============================================================================
 #include <QString>
+#include <QVariant>
 #include <QKeySequence>
 class QSettings;
 
@@ -32,33 +33,33 @@
 #define MAX_INI_LINE 512
 #define MAX_CONFIG 512
 
-#define cfg(T, NAME, DEFAULT) T##config NAME (DEFAULT, #NAME, #DEFAULT)
-#define extern_cfg(T, NAME)   extern T##config NAME
+#define cfg(T, NAME, DEFAULT) T##Config NAME (DEFAULT, #NAME, #DEFAULT)
+#define extern_cfg(T, NAME)   extern T##Config NAME
 
 // =========================================================
-class config {
+class Config {
 public:
 	enum Type {
-		Type_none,
-		Type_int,
-		Type_str,
-		Type_float,
-		Type_bool,
-		Type_keyseq,
+		Int,
+		String,
+		Float,
+		Bool,
+		KeySequence,
+		List,
 	};
 
-	config (const char* defstring) : m_defstring (defstring) {}
+	Config (const char* name, const char* defstring);
 	const char* name;
 
 	virtual Type getType() const {
-		return Type_none;
+		return (Type) 0;
 	}
 	
-	str toString() const;
-	str defaultString() const;
 	virtual void resetValue() {}
-	virtual void loadFromConfig (const QSettings* cfg) { (void) cfg; }
+	virtual void loadFromVariant (const QVariant& val) { (void) val; }
 	virtual bool isDefault() const { return false; }
+	virtual QVariant toVariant() const { return QVariant(); }
+	virtual QVariant defaultVariant() const { return QVariant(); }
 	
 	// ------------------------------------------
 	static bool load();
@@ -67,38 +68,53 @@
 	static str dirpath();
 	static str filepath (str file);
 	
+protected:
+	static void addToArray (Config* ptr);
+	
 private:
 	const char* m_defstring;
 };
 
-void addConfig (config* ptr);
+// =============================================================================
+#define IMPLEMENT_CONFIG(NAME, T) \
+	T value, defval; \
+	NAME##Config (T defval, const char* name, const char* defstring) : \
+		Config (name, defstring), value (defval), defval (defval) \
+		{ Config::addToArray (this); } \
+	\
+	operator const T&() const { return value; } \
+	Config::Type getType() const override { return Config::NAME; } \
+	virtual void resetValue() override { value = defval; } \
+	virtual bool isDefault() const override { return value == defval; } \
+	virtual QVariant toVariant() const override { return QVariant::fromValue<T> (value); } \
+	virtual QVariant defaultVariant() const override { return QVariant::fromValue<T> (defval); } \
+	virtual void loadFromVariant (const QVariant& val) override { value = val.value<T>(); } \
 
-// =============================================================================
 #define DEFINE_UNARY_OPERATOR(T, OP) \
 	T operator OP() { \
 		return OP value; \
-	} \
-	 
+	}
+
 #define DEFINE_BINARY_OPERATOR(T, OP) \
 	T operator OP (const T other) { \
 		return value OP other; \
-	} \
-	 
+	}
+
 #define DEFINE_ASSIGN_OPERATOR(T, OP) \
 	T& operator OP (const T other) { \
 		return value OP other; \
-	} \
-	 
+	}
+
 #define DEFINE_COMPARE_OPERATOR(T, OP) \
 	bool operator OP (const T other) { \
 		return value OP other; \
-	} \
-	 
+	}
+
 #define DEFINE_CAST_OPERATOR(T) \
 	operator T() { \
 		return (T) value; \
-	} \
-	 
+	}
+
 #define DEFINE_ALL_COMPARE_OPERATORS(T) \
 	DEFINE_COMPARE_OPERATOR (T, ==) \
 	DEFINE_COMPARE_OPERATOR (T, !=) \
@@ -106,42 +122,17 @@
 	DEFINE_COMPARE_OPERATOR (T, <) \
 	DEFINE_COMPARE_OPERATOR (T, >=) \
 	DEFINE_COMPARE_OPERATOR (T, <=) \
-	 
+
 #define DEFINE_INCREMENT_OPERATORS(T) \
 	T operator++() { return ++value; } \
 	T operator++(int) { return value++; } \
 	T operator--() { return --value; } \
 	T operator--(int) { return value--; }
 
-#define CONFIGTYPE(T) \
-	class T##config : public config
-
-#define IMPLEMENT_CONFIG(T) \
-	T value, defval; \
-	\
-	T##config (T _defval, const char* _name, const char* defstring) : config (defstring) { \
-		value = defval = _defval; \
-		name = _name; \
-		addConfig (this); \
-	} \
-	operator const T&() const { \
-		return value; \
-	} \
-	config::Type getType() const override { \
-		return config::Type_##T; \
-	} \
-	virtual void resetValue() { \
-		value = defval; \
-	} \
-	virtual bool isDefault() const { \
-		return value == defval; \
-	} \
-	virtual void loadFromConfig (const QSettings* cfg) override;
-
 // =============================================================================
-CONFIGTYPE (int) {
+class IntConfig : public Config {
 public:
-	IMPLEMENT_CONFIG (int)
+	IMPLEMENT_CONFIG (Int, int)
 	DEFINE_ALL_COMPARE_OPERATORS (int)
 	DEFINE_INCREMENT_OPERATORS (int)
 	DEFINE_BINARY_OPERATOR (int, +)
@@ -169,24 +160,24 @@
 };
 
 // =============================================================================
-CONFIGTYPE (str) {
+class StringConfig : public Config {
 public:
-	IMPLEMENT_CONFIG (str)
-
+	IMPLEMENT_CONFIG (String, str)
+	
 	DEFINE_COMPARE_OPERATOR (str, ==)
 	DEFINE_COMPARE_OPERATOR (str, !=)
 	DEFINE_ASSIGN_OPERATOR (str, =)
 	DEFINE_ASSIGN_OPERATOR (str, +=)
 	
-	qchar operator[] (int n) {
+	QChar operator[] (int n) {
 		return value[n];
 	}
 };
 
 // =============================================================================
-CONFIGTYPE (float) {
+class FloatConfig : public Config {
 public:
-	IMPLEMENT_CONFIG (float)
+	IMPLEMENT_CONFIG (Float, float)
 	DEFINE_ALL_COMPARE_OPERATORS (float)
 	DEFINE_INCREMENT_OPERATORS (float)
 	DEFINE_BINARY_OPERATOR (float, +)
@@ -200,21 +191,45 @@
 };
 
 // =============================================================================
-CONFIGTYPE (bool) {
+class BoolConfig : public Config {
 public:
-	IMPLEMENT_CONFIG (bool)
+	IMPLEMENT_CONFIG (Bool, bool)
 	DEFINE_ALL_COMPARE_OPERATORS (bool)
 	DEFINE_ASSIGN_OPERATOR (bool, =)
 };
 
 // =============================================================================
-typedef QKeySequence keyseq;
+class KeySequenceConfig : public Config {
+public:
+	IMPLEMENT_CONFIG (KeySequence, QKeySequence)
+	DEFINE_ALL_COMPARE_OPERATORS (QKeySequence)
+	DEFINE_ASSIGN_OPERATOR (QKeySequence, =)
+};
 
-CONFIGTYPE (keyseq) {
+// =============================================================================
+class ListConfig : public Config {
 public:
-	IMPLEMENT_CONFIG (keyseq)
-	DEFINE_ALL_COMPARE_OPERATORS (keyseq)
-	DEFINE_ASSIGN_OPERATOR (keyseq, =)
+	IMPLEMENT_CONFIG (List, QList<QVariant>)
+	DEFINE_ASSIGN_OPERATOR (QList<QVariant>, =)
+	
+	typedef QList<QVariant>::iterator it;
+	typedef QList<QVariant>::const_iterator c_it;
+	
+	it begin() {
+		return value.begin();
+	}
+	
+	c_it begin() const {
+		return value.constBegin();
+	}
+	
+	it end() {
+		return value.end();
+	}
+	
+	c_it end() const {
+		return value.constEnd();
+	}
 };
 
 #endif // CONFIG_H
\ No newline at end of file
--- a/src/configDialog.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/configDialog.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -1,19 +1,23 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *  =====================================================================
+ *
+ *  configDialog.cpp: Settings dialog and everything related to it.
+ *  Actual configuration core is in config.cpp.
  */
 
 #include <QGridLayout>
@@ -25,7 +29,6 @@
 #include <QDoubleSpinBox>
 #include <QLineEdit>
 #include <QCheckBox>
-
 #include "common.h"
 #include "configDialog.h"
 #include "file.h"
@@ -36,93 +39,73 @@
 #include "gldraw.h"
 #include "ui_config.h"
 
-extern_cfg (str, gl_bgcolor);
-extern_cfg (str, gl_maincolor);
-extern_cfg (bool, lv_colorize);
-extern_cfg (bool, gl_colorbfc);
-extern_cfg (float, gl_maincolor_alpha);
-extern_cfg (int, gl_linethickness);
-extern_cfg (str, gui_colortoolbar);
-extern_cfg (bool, edit_schemanticinline);
-extern_cfg (bool, gl_blackedges);
-extern_cfg (bool, gui_implicitfiles);
-extern_cfg (str, net_downloadpath);
-extern_cfg (bool, net_guesspaths);
-extern_cfg (bool, net_autoclose);
+extern_cfg (String, gl_bgcolor);
+extern_cfg (String, gl_maincolor);
+extern_cfg (Bool, lv_colorize);
+extern_cfg (Bool, gl_colorbfc);
+extern_cfg (Float, gl_maincolor_alpha);
+extern_cfg (Int, gl_linethickness);
+extern_cfg (String, gui_colortoolbar);
+extern_cfg (Bool, edit_schemanticinline);
+extern_cfg (Bool, gl_blackedges);
+extern_cfg (Bool, gui_implicitfiles);
+extern_cfg (String, net_downloadpath);
+extern_cfg (Bool, net_guesspaths);
+extern_cfg (Bool, net_autoclose);
+extern_cfg (Bool, gl_logostuds);
+extern_cfg (String, ld_defaultname);
+extern_cfg (String, ld_defaultuser);
+extern_cfg (Int, ld_defaultlicense);
+extern_cfg (String, prog_ytruder);
+extern_cfg (String, prog_rectifier);
+extern_cfg (String, prog_intersector);
+extern_cfg (String, prog_coverer);
+extern_cfg (String, prog_isecalc);
+extern_cfg (String, prog_edger2);
+extern_cfg (Bool, prog_ytruder_wine);
+extern_cfg (Bool, prog_rectifier_wine);
+extern_cfg (Bool, prog_intersector_wine);
+extern_cfg (Bool, prog_coverer_wine);
+extern_cfg (Bool, prog_isecalc_wine);
+extern_cfg (Bool, prog_edger2_wine);
 
-extern_cfg (str, prog_ytruder);
-extern_cfg (str, prog_rectifier);
-extern_cfg (str, prog_intersector);
-extern_cfg (str, prog_coverer);
-extern_cfg (str, prog_isecalc);
-extern_cfg (str, prog_edger2);
-extern_cfg (bool, prog_ytruder_wine);
-extern_cfg (bool, prog_rectifier_wine);
-extern_cfg (bool, prog_intersector_wine);
-extern_cfg (bool, prog_coverer_wine);
-extern_cfg (bool, prog_isecalc_wine);
-extern_cfg (bool, prog_edger2_wine);
-
-#define act(N) extern_cfg (keyseq, key_##N);
+#define act(N) extern_cfg (KeySequence, key_##N);
 #include "actions.h"
 
+const char* g_extProgPathFilter =
+#ifdef _WIN32
+	"Applications (*.exe)(*.exe);;All files (*.*)(*.*)";
+#else
+	"";
+#endif
+
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
-ConfigDialog::ConfigDialog (ForgeWindow* parent) : QDialog (parent) {
+// -----------------------------------------------------------------------------
+ConfigDialog::ConfigDialog (ConfigDialog::Tab deftab, QWidget* parent, Qt::WindowFlags f) :
+	QDialog (parent, f)
+{
+	assert (g_win);
 	ui = new Ui_ConfigUI;
 	ui->setupUi (this);
 	
-	initMainTab();
-	initShortcutsTab();
-	initQuickColorTab();
-	initGridTab();
-	initExtProgTab();
-	
-	ui->downloadPath->setText (net_downloadpath);
-	ui->guessNetPaths->setChecked (net_guesspaths);
-	ui->autoCloseNetPrompt->setChecked (net_autoclose);
-	connect (ui->findDownloadPath, SIGNAL (clicked(bool)), this, SLOT (slot_findDownloadFolder()));
-}
-
-// =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
-ConfigDialog::~ConfigDialog() {
-	delete ui;
-}
-
-// =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
-void ConfigDialog::initMainTab() {
-	// Init color stuff
+	// Interface tab
 	setButtonBackground (ui->backgroundColorButton, gl_bgcolor);
 	connect (ui->backgroundColorButton, SIGNAL (clicked()),
 		this, SLOT (slot_setGLBackground()));
 	
-	setButtonBackground (ui->mainColorButton, gl_maincolor.value);
+	setButtonBackground (ui->mainColorButton, gl_maincolor);
 	connect (ui->mainColorButton, SIGNAL (clicked()),
 		this, SLOT (slot_setGLForeground()));
 	
-	// Sliders
 	ui->mainColorAlpha->setValue (gl_maincolor_alpha * 10.0f);
 	ui->lineThickness->setValue (gl_linethickness);
-	
-	// Checkboxes
 	ui->colorizeObjects->setChecked (lv_colorize);
 	ui->colorBFC->setChecked (gl_colorbfc);
 	ui->blackEdges->setChecked (gl_blackedges);
-	// ui->scemanticInlining->setChecked (edit_schemanticinline);
 	ui->implicitFiles->setChecked (gui_implicitfiles);
-}
-
-// =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
-void ConfigDialog::initShortcutsTab() {
+	ui->m_logostuds->setChecked (gl_logostuds);
+	
 	ulong i = 0;
-	
 #define act(N) addShortcut (key_##N, ACTION(N), i);
 #include "actions.h"
 	
@@ -132,9 +115,45 @@
 	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()));
+	
+	quickColors = quickColorsFromConfig();
+	updateQuickColorList();
+	
+	connect (ui->quickColor_add, SIGNAL (clicked()), this, SLOT (slot_setColor()));
+	connect (ui->quickColor_remove, SIGNAL (clicked()), this, SLOT (slot_delColor()));
+	connect (ui->quickColor_edit, SIGNAL (clicked()), this, SLOT (slot_setColor()));
+	connect (ui->quickColor_addSep, SIGNAL (clicked()), this, SLOT (slot_addColorSeparator()));
+	connect (ui->quickColor_moveUp, SIGNAL (clicked()), this, SLOT (slot_moveColor()));
+	connect (ui->quickColor_moveDown, SIGNAL (clicked()), this, SLOT (slot_moveColor()));
+	connect (ui->quickColor_clear, SIGNAL (clicked()), this, SLOT (slot_clearColors()));
+	
+	ui->downloadPath->setText (net_downloadpath);
+	ui->guessNetPaths->setChecked (net_guesspaths);
+	ui->autoCloseNetPrompt->setChecked (net_autoclose);
+	connect (ui->findDownloadPath, SIGNAL (clicked(bool)), this, SLOT (slot_findDownloadFolder()));
+	
+	ui->m_profileName->setText (ld_defaultname);
+	ui->m_profileUsername->setText (ld_defaultuser);
+	ui->m_profileLicense->setCurrentIndex (ld_defaultlicense);
+	ui->tabs->setCurrentIndex (deftab);
+	
+	initGrids();
+	initExtProgs();
+	
+	connect (ui->buttonBox, SIGNAL (clicked (QAbstractButton*)),
+		this, SLOT (buttonClicked(QAbstractButton*)));
 }
 
-void ConfigDialog::addShortcut (keyseqconfig& cfg, QAction* act, ulong& i) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+ConfigDialog::~ConfigDialog() {
+	delete ui;
+}
+
+// =============================================================================
+// Adds a shortcut entry to the list of shortcuts.
+// -----------------------------------------------------------------------------
+void ConfigDialog::addShortcut (KeySequenceConfig& cfg, QAction* act, ulong& i) {
 	ShortcutListItem* item = new ShortcutListItem;
 	item->setIcon (act->icon());
 	item->setKeyConfig (&cfg);
@@ -150,25 +169,9 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
-void ConfigDialog::initQuickColorTab() {
-	quickColors = quickColorsFromConfig();
-	updateQuickColorList();
-	
-	connect (ui->quickColor_add, SIGNAL (clicked()), this, SLOT (slot_setColor()));
-	connect (ui->quickColor_remove, SIGNAL (clicked()), this, SLOT (slot_delColor()));
-	connect (ui->quickColor_edit, SIGNAL (clicked()), this, SLOT (slot_setColor()));
-	connect (ui->quickColor_addSep, SIGNAL (clicked()), this, SLOT (slot_addColorSeparator()));
-	connect (ui->quickColor_moveUp, SIGNAL (clicked()), this, SLOT (slot_moveColor()));
-	connect (ui->quickColor_moveDown, SIGNAL (clicked()), this, SLOT (slot_moveColor()));
-	connect (ui->quickColor_clear, SIGNAL (clicked()), this, SLOT (slot_clearColors()));
-}
-
-// =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
-void ConfigDialog::initGridTab() {
+// Initializes the table of grid stuff
+// -----------------------------------------------------------------------------
+void ConfigDialog::initGrids() {
 	QGridLayout* gridlayout = new QGridLayout;
 	QLabel* xlabel = new QLabel ("X"),
 		*ylabel = new QLabel ("Y"),
@@ -201,20 +204,19 @@
 			gridlayout->addWidget (dsb_gridData[i][j], i + 1, j + 1);
 		}
 	}
-
+	
 	ui->grids->setLayout (gridlayout);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 static const struct extProgInfo {
 	const str name, iconname;
-	strconfig* const path;
+	StringConfig* const path;
 	mutable QLineEdit* input;
 	mutable QPushButton* setPathButton;
 #ifndef _WIN32
-	boolconfig* const wine;
+	BoolConfig* const wine;
 	mutable QCheckBox* wineBox;
 #endif // _WIN32
 } g_extProgInfo[] = {
@@ -232,7 +234,10 @@
 #undef EXTPROG
 };
 
-void ConfigDialog::initExtProgTab() {
+// =============================================================================
+// Initializes the stuff in the ext programs tab
+// -----------------------------------------------------------------------------
+void ConfigDialog::initExtProgs() {
 	QGridLayout* pathsLayout = new QGridLayout;
 	ulong row = 0;
 	
@@ -264,13 +269,80 @@
 		
 		++row;
 	}
-
+	
 	ui->extProgs->setLayout (pathsLayout);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// Set the settings based on widget data.
+// -----------------------------------------------------------------------------
+void ConfigDialog::applySettings() {
+	// Apply configuration
+	lv_colorize = ui->colorizeObjects->isChecked();
+	gl_colorbfc = ui->colorBFC->isChecked();
+	gl_blackedges = ui->blackEdges->isChecked();
+	gl_maincolor_alpha = ((double) ui->mainColorAlpha->value()) / 10.0f;
+	gl_linethickness = ui->lineThickness->value();
+	gui_implicitfiles = ui->implicitFiles->isChecked();
+	net_downloadpath = ui->downloadPath->text();
+	net_guesspaths = ui->guessNetPaths->isChecked();
+	net_autoclose = ui->autoCloseNetPrompt->isChecked();
+	gl_logostuds = ui->m_logostuds->isChecked();
+	ld_defaultuser = ui->m_profileUsername->text();
+	ld_defaultname = ui->m_profileName->text();
+	ld_defaultlicense = ui->m_profileLicense->currentIndex();
+	
+	// Rebuild the quick color toolbar
+	g_win->setQuickColors (quickColors);
+	gui_colortoolbar = quickColorString();
+	
+	// Set the grid settings
+	for (int i = 0; i < g_NumGrids; ++i)
+		for (int j = 0; j < 4; ++j)
+			g_GridInfo[i].confs[j]->value = dsb_gridData[i][j]->value();
+	
+	// Apply key shortcuts
+#define act(N) ACTION(N)->setShortcut (key_##N);
+#include "actions.h"
+	
+	// Ext program settings
+	for (const extProgInfo & info : g_extProgInfo) {
+		*info.path = info.input->text();
+		
+#ifndef _WIN32
+		*info.wine = info.wineBox->isChecked();
+#endif // _WIN32
+	}
+	
+	Config::save();
+	reloadAllSubfiles();
+	loadLogoedStuds();
+	g_win->R()->setBackground();
+	g_win->fullRefresh();
+	g_win->updateToolBars();
+	g_win->updateFileList();
+}
+
 // =============================================================================
+// A dialog button was clicked
+// -----------------------------------------------------------------------------
+void ConfigDialog::buttonClicked (QAbstractButton* button) {
+	typedef QDialogButtonBox QDDB;
+	QDialogButtonBox* dbb = ui->buttonBox;
+	
+	if (button == dbb->button (QDDB::Ok)) {
+		applySettings();
+		accept();
+	} elif (button == dbb->button (QDDB::Apply)) {
+		applySettings();
+	} elif (button == dbb->button (QDDB::Cancel)) {
+		reject();
+	}
+}
+
+// =============================================================================
+// Update the list of color toolbar items in the quick color tab.
+// -----------------------------------------------------------------------------
 void ConfigDialog::updateQuickColorList (LDQuickColor* sel) {
 	for (QListWidgetItem* item : quickColorItems)
 		delete item;
@@ -281,11 +353,11 @@
 	for (LDQuickColor& entry : quickColors) {
 		QListWidgetItem* item = new QListWidgetItem;
 		
-		if (entry.isSeparator) {
+		if (entry.isSeparator()) {
 			item->setText ("--------");
 			item->setIcon (getIcon ("empty"));
 		} else {
-			LDColor* col = entry.col;
+			LDColor* col = entry.color();
 			
 			if (col == null) {
 				item->setText ("[[unknown color]]");
@@ -307,8 +379,8 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// Quick colors: add or edit button was clicked.
+// -----------------------------------------------------------------------------
 void ConfigDialog::slot_setColor() {
 	LDQuickColor* entry = null;
 	QListWidgetItem* item = null;
@@ -323,20 +395,20 @@
 		ulong i = getItemRow (item, quickColorItems);
 		entry = &quickColors[i];
 		
-		if (entry->isSeparator == true)
+		if (entry->isSeparator() == true)
 			return; // don't color separators
 	}
 	
-	short defval = entry ? entry->col->index : -1;
+	short defval = entry ? entry->color()->index : -1;
 	short val;
 	
 	if (ColorSelector::getColor (val, defval, this) == false)
 		return;
 	
 	if (entry)
-		entry->col = getColor (val);
+		entry->setColor (getColor (val));
 	else {
-		LDQuickColor entry = {getColor (val), null, false};
+		LDQuickColor entry (getColor (val), null);
 		
 		item = getSelectedQuickColor();
 		ulong idx;
@@ -354,8 +426,8 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// Remove a quick color
+// -----------------------------------------------------------------------------
 void ConfigDialog::slot_delColor() {
 	if (ui->quickColorList->selectedItems().size() == 0)
 		return;
@@ -366,6 +438,8 @@
 }
 
 // =============================================================================
+// Move a quick color up/down
+// -----------------------------------------------------------------------------
 void ConfigDialog::slot_moveColor() {
 	const bool up = (static_cast<QPushButton*> (sender()) == ui->quickColor_moveUp);
 	
@@ -387,21 +461,25 @@
 }
 
 // =============================================================================
+// Add a separator to quick colors
+// -----------------------------------------------------------------------------
 void ConfigDialog::slot_addColorSeparator() {
-	quickColors << LDQuickColor ({null, null, true});
+	quickColors << LDQuickColor::getSeparator();
 	updateQuickColorList (&quickColors[quickColors.size() - 1]);
 }
 
 // =============================================================================
+// Clear all quick colors
+// -----------------------------------------------------------------------------
 void ConfigDialog::slot_clearColors() {
 	quickColors.clear();
 	updateQuickColorList();
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
-void ConfigDialog::pickColor (strconfig& conf, QPushButton* button) {
+// Pick a color and set the appropriate configuration option.
+// -----------------------------------------------------------------------------
+void ConfigDialog::pickColor (StringConfig& conf, QPushButton* button) {
 	QColor col = QColorDialog::getColor (QColor (conf));
 	
 	if (col.isValid()) {
@@ -413,17 +491,21 @@
 	}
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void ConfigDialog::slot_setGLBackground() {
 	pickColor (gl_bgcolor, ui->backgroundColorButton);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void ConfigDialog::slot_setGLForeground() {
 	pickColor (gl_maincolor, ui->mainColorButton);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// Sets background color of a given button.
+// -----------------------------------------------------------------------------
 void ConfigDialog::setButtonBackground (QPushButton* button, str value) {
 	button->setIcon (getIcon ("colorselect"));
 	button->setAutoFillBackground (true);
@@ -431,6 +513,8 @@
 }
 
 // =============================================================================
+// Finds the given list widget item in the list of widget items given.
+// -----------------------------------------------------------------------------
 int ConfigDialog::getItemRow (QListWidgetItem* item, List<QListWidgetItem*>& haystack) {
 	int i = 0;
 	
@@ -444,16 +528,19 @@
 }
 
 // =============================================================================
+// Which quick color is currently selected?
+// -----------------------------------------------------------------------------
 QListWidgetItem* ConfigDialog::getSelectedQuickColor() {
 	if (ui->quickColorList->selectedItems().size() == 0)
 		return null;
 	
-	return ui->quickColorList->selectedItems() [0];
+	return ui->quickColorList->selectedItems()[0];
 }
 
 // =============================================================================
-QList<ShortcutListItem*> ConfigDialog::getShortcutSelection()
-{
+// Get the list of shortcuts selected
+// -----------------------------------------------------------------------------
+QList<ShortcutListItem*> ConfigDialog::getShortcutSelection() {
 	QList<ShortcutListItem*> out;
 	
 	for (QListWidgetItem* entry : ui->shortcutsList->selectedItems())
@@ -463,8 +550,9 @@
 }
 
 // =============================================================================
-void ConfigDialog::slot_setShortcut()
-{
+// Edit the shortcut of a given action.
+// -----------------------------------------------------------------------------
+void ConfigDialog::slot_setShortcut() {
 	QList<ShortcutListItem*> sel = getShortcutSelection();
 	
 	if (sel.size() < 1)
@@ -477,8 +565,9 @@
 }
 
 // =============================================================================
-void ConfigDialog::slot_resetShortcut()
-{
+// Reset a shortcut to defaults
+// -----------------------------------------------------------------------------
+void ConfigDialog::slot_resetShortcut() {
 	QList<ShortcutListItem*> sel = getShortcutSelection();
 	
 	for (ShortcutListItem* item : sel) {
@@ -488,6 +577,8 @@
 }
 
 // =============================================================================
+// Remove the shortcut of an action.
+// -----------------------------------------------------------------------------
 void ConfigDialog::slot_clearShortcut() {
 	QList<ShortcutListItem*> sel = getShortcutSelection();
 	
@@ -498,6 +589,8 @@
 }
 
 // =============================================================================
+// Set the path of an external program
+// -----------------------------------------------------------------------------
 void ConfigDialog::slot_setExtProgPath() {
 	const extProgInfo* info = null;
 	
@@ -509,21 +602,16 @@
 	}
 	
 	assert (info != null);
-	
-	str filter;
-#ifdef _WIN32
-	filter = "Applications (*.exe)(*.exe);;All files (*.*)(*.*)";
-#endif // WIN32
+	str fpath = QFileDialog::getOpenFileName (this, fmt ("Path to %1", info->name), *info->path, g_extProgPathFilter);
 	
-	str fpath = QFileDialog::getOpenFileName (this, fmt ("Path to %1", info->name), *info->path, filter);
-
-	if (fpath.length() == 0)
+	if (fpath.isEmpty())
 		return;
-
+	
 	info->input->setText (fpath);
 }
 
 // =============================================================================
+// '...' button pressed for the download path
 // -----------------------------------------------------------------------------
 void ConfigDialog::slot_findDownloadFolder() {
 	str dpath = QFileDialog::getExistingDirectory();
@@ -531,8 +619,8 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// Updates the text string for a given shortcut list item
+// -----------------------------------------------------------------------------
 void ConfigDialog::setShortcutText (ShortcutListItem* item) {
 	QAction* act = item->action();
 	str label = act->iconText();
@@ -541,85 +629,24 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// Gets the configuration string of the quick color toolbar
+// -----------------------------------------------------------------------------
 str ConfigDialog::quickColorString() {
 	str val;
 	
-	for (LDQuickColor entry : quickColors) {
+	for (const LDQuickColor& entry : quickColors) {
 		if (val.length() > 0)
 			val += ':';
 		
-		if (entry.isSeparator)
+		if (entry.isSeparator())
 			val += '|';
 		else
-			val += fmt ("%1", entry.col->index);
+			val += fmt ("%1", entry.color()->index);
 	}
 	
 	return val;
 }
 
-const Ui_ConfigUI* ConfigDialog::getUI() const {
-	return ui;
-}
-
-float ConfigDialog::getGridValue (int i, int j) const {
-	return dsb_gridData[i][j]->value();
-}
-
-// =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
-void ConfigDialog::staticDialog() {
-	ConfigDialog dlg (g_win);
-	
-	if (dlg.exec()) {
-		// Apply configuration
-		lv_colorize = dlg.getUI()->colorizeObjects->isChecked();
-		gl_colorbfc = dlg.getUI()->colorBFC->isChecked();
-		// edit_schemanticinline = dlg.getUI()->scemanticInlining->isChecked();
-		gl_blackedges = dlg.getUI()->blackEdges->isChecked();
-		gl_maincolor_alpha = ((double) dlg.getUI()->mainColorAlpha->value()) / 10.0f;
-		gl_linethickness = dlg.getUI()->lineThickness->value();
-		gui_implicitfiles = dlg.getUI()->implicitFiles->isChecked();
-		net_downloadpath = dlg.getUI()->downloadPath->text();
-		net_guesspaths = dlg.getUI()->guessNetPaths->isChecked();
-		net_autoclose = dlg.getUI()->autoCloseNetPrompt->isChecked();
-		
-		if (net_downloadpath.value.right (1) != DIRSLASH)
-			net_downloadpath += DIRSLASH;
-		
-		// Rebuild the quick color toolbar
-		g_win->setQuickColors (dlg.quickColors);
-		gui_colortoolbar = dlg.quickColorString();
-		
-		// Set the grid settings
-		for (int i = 0; i < g_NumGrids; ++i)
-			for (int j = 0; j < 4; ++j)
-				g_GridInfo[i].confs[j]->value = dlg.getGridValue (i, j);
-		
-		// Apply key shortcuts
-#define act(N) ACTION(N)->setShortcut (key_##N);
-#include "actions.h"
-		
-		// Ext program settings
-		for (const extProgInfo & info : g_extProgInfo) {
-			*info.path = info.input->text();
-			
-#ifndef _WIN32
-			*info.wine = info.wineBox->isChecked();
-#endif // _WIN32
-		}
-		
-		config::save();
-		reloadAllSubfiles();
-		g_win->R()->setBackground();
-		g_win->fullRefresh();
-		g_win->updateToolBars();
-		g_win->updateFileList();
-	}
-}
-
 // =========================================================================================================================
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =========================================================================================================================
@@ -645,9 +672,8 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
-bool KeySequenceDialog::staticDialog (keyseqconfig* cfg, QWidget* parent) {
+// -----------------------------------------------------------------------------
+bool KeySequenceDialog::staticDialog (KeySequenceConfig* cfg, QWidget* parent) {
 	KeySequenceDialog dlg (cfg->value, parent);
 	
 	if (dlg.exec() == false)
@@ -658,8 +684,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void KeySequenceDialog::updateOutput() {
 	str shortcut = seq.toString();
 	
@@ -671,11 +696,8 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void KeySequenceDialog::keyPressEvent (QKeyEvent* ev) {
 	seq = ev->key() + ev->modifiers();
 	updateOutput();
-}
-
-#include "build/moc_configDialog.cpp"
\ No newline at end of file
+}
\ No newline at end of file
--- a/src/configDialog.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/configDialog.h	Sat Sep 07 13:23:09 2013 +0300
@@ -28,7 +28,7 @@
 
 // =============================================================================
 class ShortcutListItem : public QListWidgetItem {
-	PROPERTY (keyseqconfig*, keyConfig, setKeyConfig)
+	PROPERTY (KeySequenceConfig*, keyConfig, setKeyConfig)
 	PROPERTY (QAction*, action, setAction)
 	
 public:
@@ -41,10 +41,18 @@
 	Q_OBJECT
 	
 public:
-	ConfigDialog (ForgeWindow* parent);
+	enum Tab {
+		InterfaceTab,
+		ProfileTab,
+		ShortcutsTab,
+		QuickColorsTab,
+		GridsTab,
+		ExtProgsTab,
+		DownloadTab
+	};
+	
+	explicit ConfigDialog (Tab deftab = InterfaceTab, QWidget* parent = null, Qt::WindowFlags f = 0);
 	virtual ~ConfigDialog();
-	static void staticDialog();
-	const Ui_ConfigUI* getUI() const;
 	float getGridValue (int i, int j) const;
 	
 	List<LDQuickColor> quickColors;
@@ -56,20 +64,18 @@
 	QLabel* lb_gridIcons[3];
 	List<QListWidgetItem*> quickColorItems;
 	
-	void initMainTab();
-	void initShortcutsTab();
-	void initQuickColorTab();
-	void initGridTab();
-	void initExtProgTab();
-	void addShortcut (keyseqconfig& cfg, QAction* act, ulong& i);
+	void applySettings();
+	void addShortcut (KeySequenceConfig& cfg, QAction* act, ulong& i);
 	void setButtonBackground (QPushButton* button, str value);
-	void pickColor (strconfig& cfg, QPushButton* button);
+	void pickColor (StringConfig& cfg, QPushButton* button);
 	void updateQuickColorList (LDQuickColor* sel = null);
 	void setShortcutText (ShortcutListItem* item);
 	int getItemRow (QListWidgetItem* item, List<QListWidgetItem*>& haystack);
 	str quickColorString();
 	QListWidgetItem* getSelectedQuickColor();
 	QList<ShortcutListItem*> getShortcutSelection();
+	void initGrids();
+	void initExtProgs();
 	
 private slots:
 	void slot_setGLBackground();
@@ -84,6 +90,7 @@
 	void slot_clearColors();
 	void slot_setExtProgPath();
 	void slot_findDownloadFolder();
+	void buttonClicked (QAbstractButton* button);
 };
 
 // =============================================================================
@@ -94,7 +101,7 @@
 
 public:
 	explicit KeySequenceDialog (QKeySequence seq, QWidget* parent = null, Qt::WindowFlags f = 0);
-	static bool staticDialog (keyseqconfig* cfg, QWidget* parent = null);
+	static bool staticDialog (KeySequenceConfig* cfg, QWidget* parent = null);
 	
 	QLabel* lb_output;
 	QDialogButtonBox* bbx_buttons;
--- a/src/dialogs.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/dialogs.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -27,6 +27,8 @@
 #include <QGridLayout>
 #include <QProgressBar>
 #include <QCheckBox>
+#include <QDesktopServices>
+#include <QUrl>
 
 #include "dialogs.h"
 #include "widgets.h"
@@ -38,10 +40,14 @@
 #include "ui_overlay.h"
 #include "ui_ldrawpath.h"
 #include "ui_openprogress.h"
+#include "ui_extprogpath.h"
+#include "ui_about.h"
 
-extern_cfg (str, io_ldpath);
+extern const char* g_extProgPathFilter;
+extern_cfg (String, io_ldpath);
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 OverlayDialog::OverlayDialog (QWidget* parent, Qt::WindowFlags f) : QDialog (parent, f) {
 	ui = new Ui_OverlayUI;
 	ui->setupUi (this);
@@ -69,10 +75,14 @@
 	fillDefaults (cam);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 OverlayDialog::~OverlayDialog() {
 	delete ui;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void OverlayDialog::fillDefaults (int newcam) {
 	overlayMeta& info = g_win->R()->getOverlay (newcam);
 	radioDefault<int> (newcam, m_cameraArgs);
@@ -92,6 +102,8 @@
 	}
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 str OverlayDialog::fpath() const {
 	return ui->filename->text();
 }
@@ -129,11 +141,12 @@
 	ui->buttonBox->button (QDialogButtonBox::Ok)->setEnabled (enable);
 }
 
-// =================================================================================================
+// =============================================================================
+// -----------------------------------------------------------------------------
 LDrawPathDialog::LDrawPathDialog (const bool validDefault, QWidget* parent, Qt::WindowFlags f) :
 	QDialog (parent, f),
-	m_validDefault (validDefault) {
-	
+	m_validDefault (validDefault)
+{
 	ui = new Ui_LDPathUI;
 	ui->setupUi (this);
 	ui->status->setText ("---");
@@ -158,6 +171,8 @@
 		slot_tryConfigure();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 LDrawPathDialog::~LDrawPathDialog() {
 	delete ui;
 }
@@ -178,6 +193,8 @@
 	return ui->path->text();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void LDrawPathDialog::slot_findPath() {
 	str newpath = QFileDialog::getExistingDirectory (this, "Find LDraw Path");
 	
@@ -187,29 +204,34 @@
 	}
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void LDrawPathDialog::slot_exit() {
 	exit (1);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void LDrawPathDialog::slot_tryConfigure() {
 	if (LDPaths::tryConfigure (filename()) == false) {
 		ui->status->setText (fmt ("<span style=\"color:#700; \">%1</span>", LDPaths::getError()));
 		okButton()->setEnabled (false);
 		return;
 	}
-
+	
 	ui->status->setText ("<span style=\"color: #270; \">OK!</span>");
 	okButton()->setEnabled (true);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void LDrawPathDialog::slot_accept() {
-	config::save();
+	Config::save();
 	accept();
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 OpenProgressDialog::OpenProgressDialog (QWidget* parent, Qt::WindowFlags f) : QDialog (parent, f) {
 	ui = new Ui_OpenProgressUI;
 	ui->setupUi (this);
@@ -219,29 +241,96 @@
 	m_progress = 0;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 OpenProgressDialog::~OpenProgressDialog() {
 	delete ui;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 READ_ACCESSOR (ulong, OpenProgressDialog::numLines) {
 	return m_numLines;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 SET_ACCESSOR (ulong, OpenProgressDialog::setNumLines) {
 	m_numLines = val;
 	ui->progressBar->setRange (0, numLines());
 	updateValues();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void OpenProgressDialog::updateValues() {
 	ui->progressText->setText (fmt ("Parsing... %1 / %2", progress(), numLines()));
 	ui->progressBar->setValue (progress());
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void OpenProgressDialog::updateProgress (int progress) {
 	m_progress = progress;
 	updateValues();
 }
 
-#include "build/moc_dialogs.cpp"
-// kate: indent-mode cstyle; indent-width 4; replace-tabs off; tab-width 4; 
+// =============================================================================
+// -----------------------------------------------------------------------------
+ExtProgPathPrompt::ExtProgPathPrompt (str progName, QWidget* parent, Qt::WindowFlags f) :
+	QDialog (parent, f),
+	ui (new Ui_ExtProgPath)
+{
+	ui->setupUi (this);
+	
+	str labelText = ui->m_label->text();
+	labelText.replace ("<PROGRAM>", progName);
+	ui->m_label->setText (labelText);
+	
+	connect (ui->m_findPath, SIGNAL (clicked (bool)), this, SLOT (findPath()));
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+ExtProgPathPrompt::~ExtProgPathPrompt() {
+	delete ui;
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+void ExtProgPathPrompt::findPath() {
+	str path = QFileDialog::getOpenFileName (null, "", "", g_extProgPathFilter);
+	
+	if (!path.isEmpty())
+		ui->m_path->setText (path);
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+str ExtProgPathPrompt::getPath() const {
+	return ui->m_path->text();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+AboutDialog::AboutDialog (QWidget* parent, Qt::WindowFlags f) :
+	QDialog (parent, f)
+{
+	Ui::AboutUI ui;
+	ui.setupUi (this);
+	ui.versionInfo->setText (fmt (tr ("LDForge %1"), fullVersionString()));
+	
+	QPushButton* mailButton = new QPushButton;
+	mailButton->setText ("Contact");
+	mailButton->setIcon (getIcon ("mail"));
+	ui.buttonBox->addButton (static_cast<QAbstractButton*> (mailButton), QDialogButtonBox::HelpRole);
+	connect (ui.buttonBox, SIGNAL (helpRequested()), this, SLOT (slot_mail()));
+	
+	setWindowTitle ("About " APPNAME);
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+void AboutDialog::slot_mail() {
+	QDesktopServices::openUrl (QUrl ("mailto:Santeri Piippo <slatenails64@gmail.com>?subject=LDForge"));
+}
\ No newline at end of file
--- a/src/dialogs.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/dialogs.h	Sat Sep 07 13:23:09 2013 +0300
@@ -23,6 +23,7 @@
 #include "common.h"
 #include "types.h"
 
+class Ui_ExtProgPath;
 class QRadioButton;
 class QCheckBox;
 class QProgressBar;
@@ -32,7 +33,7 @@
 class QPushButton;
 class QLineEdit;
 class QSpinBox;
-class RadioBox;
+class RadioGroup;
 class QLabel;
 class QAbstractButton;
 class Ui_OverlayUI;
@@ -107,5 +108,31 @@
 	void updateValues();
 };
 
-#endif // DIALOGS_H
-// kate: indent-mode cstyle; indent-width 4; replace-tabs off; tab-width 4; 
+// =============================================================================
+class ExtProgPathPrompt : public QDialog {
+	Q_OBJECT
+	
+public:
+	explicit ExtProgPathPrompt (str progName, QWidget* parent = 0, Qt::WindowFlags f = 0);
+	virtual ~ExtProgPathPrompt();
+	str getPath() const;
+	
+public slots:
+	void findPath();
+	
+private:
+	Ui_ExtProgPath* ui;
+};
+
+// =============================================================================
+class AboutDialog : public QDialog {
+	Q_OBJECT
+	
+public:
+	AboutDialog (QWidget* parent = null, Qt::WindowFlags f = 0);
+	
+private slots:
+	void slot_mail();
+};
+
+#endif // DIALOGS_H
\ No newline at end of file
--- a/src/docs.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/docs.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -23,6 +23,8 @@
 #include "common.h"
 #include "types.h"
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 class DocumentViewer : public QDialog {
 public:
 	explicit DocumentViewer (QWidget* parent = null, Qt::WindowFlags f = 0) : QDialog (parent, f) {
@@ -61,6 +63,8 @@
 	"<p>Finally, use the \"Set Overlay Image\" dialog and fill in the details. The "
 	"overlay image should then be ready for use.";
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void showDocumentation (const char* text) {
 	DocumentViewer dlg;
 	dlg.setText (text);
--- a/src/docs.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/docs.h	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
--- a/src/download.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/download.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -26,13 +26,13 @@
 #include "ui_downloadfrom.h"
 #include "types.h"
 #include "gui.h"
-#include "build/moc_download.cpp"
 #include "file.h"
 #include "gldraw.h"
+#include "configDialog.h"
 
-cfg (str, net_downloadpath, "");
-cfg (bool, net_guesspaths, true);
-cfg (bool, net_autoclose, false);
+cfg (String, net_downloadpath, "");
+cfg (Bool, net_guesspaths, true);
+cfg (Bool, net_autoclose, false);
 
 constexpr const char* PartDownloader::k_OfficialURL,
 	*PartDownloader::k_UnofficialURL;
@@ -44,6 +44,8 @@
 	if (path == "" || QDir (path).exists() == false) {
 		critical (PartDownloader::tr ("You need to specify a valid path for "
 			"downloaded files in the configuration to download paths."));
+		
+		(new ConfigDialog (ConfigDialog::DownloadTab, null))->exec();
 		return;
 	}
 	
@@ -73,7 +75,7 @@
 	
 	m_downloadButton = new QPushButton (tr ("Download"));
 	ui->buttonBox->addButton (m_downloadButton, QDialogButtonBox::ActionRole);
-	ui->buttonBox->button (QDialogButtonBox::Abort)->setEnabled (false);
+	getButton (Abort)->setEnabled (false);
 	
 	connect (ui->source, SIGNAL (currentIndexChanged (int)), this, SLOT (sourceChanged (int)));
 	connect (ui->buttonBox, SIGNAL (clicked (QAbstractButton*)), this, SLOT (buttonClicked (QAbstractButton*)));
@@ -116,8 +118,8 @@
 	
 	// Ensure .dat extension
 	if (dest.right (4) != ".dat") {
-		// Remove the existing extension, if any. It may be we're here over a
-		// typo in the .dat extension.
+		/* Remove the existing extension, if any. It may be we're here over a
+		   typo in the .dat extension. */
 		const int dotpos = dest.lastIndexOf (".");
 		if (dotpos != -1 && dotpos >= dest.length() - 4)
 			dest.chop (dest.length() - dotpos);
@@ -125,8 +127,8 @@
 		dest += ".dat";
 	}
 	
-	// If the part starts with s\ or s/, then use parts/s/. Same goes with
-	// 48\ and p/48/.
+	/* If the part starts with s\ or s/, then use parts/s/. Same goes with
+	   48\ and p/48/. */
 	if (dest.left (2) == "s\\" || dest.left (2) == "s/") {
 		dest.remove (0, 2);
 		dest.prepend ("parts/s/");
@@ -136,31 +138,30 @@
 	}
 	
 	/* Try determine where to put this part. We have four directories:
-	 * parts/, parts/s/, p/, and p/48/. If we haven't already specified
-	 * either parts/ or p/, we need to add it automatically. Part files
-	 * are numbers which can be followed by:
-	 * - c** (composites)
-	 * - d** (formed stickers)
-	 * - a lowercase alphabetic letter for variants
-	 *
-	 * Subfiles have an s** prefix, in which case we use parts/s/. Note that
-	 * the regex starts with a '^' so it won't catch already fully given part
-	 * file names.
-	 */
-	{
-		str partRegex = "^u?[0-9]+(c[0-9][0-9]+)*(d[0-9][0-9]+)*[a-z]?(p[0-9a-z][0-9a-z]+)*";
-		str subpartRegex = partRegex + "s[0-9][0-9]+";
-		
-		partRegex += "\\.dat$";
-		subpartRegex += "\\.dat$";
-		
-		if (QRegExp (subpartRegex).exactMatch (dest))
-			dest.prepend ("parts/s/");
-		elif (QRegExp (partRegex).exactMatch (dest))
-			dest.prepend ("parts/");
-		elif (dest.left (6) != "parts/" && dest.left (2) != "p/")
-			dest.prepend ("p/");
-	}
+	   parts/, parts/s/, p/, and p/48/. If we haven't already specified
+	   either parts/ or p/, we need to add it automatically. Part files
+	   are numbers wit a possible u prefix for parts with unknown number
+	   which can be followed by any of:
+	   - c** (composites)
+	   - d** (formed stickers)
+	   - p** (patterns)
+	   - a lowercase alphabetic letter for variants
+	
+	   Subfiles (usually) have an s** prefix, in which case we use parts/s/.
+	   Note that the regex starts with a '^' so it won't catch already fully
+	   given part file names. */
+	str partRegex = "^u?[0-9]+(c[0-9][0-9]+)*(d[0-9][0-9]+)*[a-z]?(p[0-9a-z][0-9a-z]+)*";
+	str subpartRegex = partRegex + "s[0-9][0-9]+";
+	
+	partRegex += "\\.dat$";
+	subpartRegex += "\\.dat$";
+	
+	if (QRegExp (subpartRegex).exactMatch (dest))
+		dest.prepend ("parts/s/");
+	elif (QRegExp (partRegex).exactMatch (dest))
+		dest.prepend ("parts/");
+	elif (dest.left (6) != "parts/" && dest.left (2) != "p/")
+		dest.prepend ("p/");
 }
 
 // =============================================================================
@@ -178,6 +179,8 @@
 		ui->fileNameLabel->setText (tr ("File name:"));
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void PartDownloader::buttonClicked (QAbstractButton* btn) {
 	if (btn == getButton (Close)) {
 		reject();
@@ -196,7 +199,7 @@
 		
 		modifyDest (dest);
 		
-		if (QFile::exists (PartDownloader::getDownloadPath() + dest)) {
+		if (QFile::exists (PartDownloader::getDownloadPath() + DIRSLASH + dest)) {
 			const str overwritemsg = fmt (tr ("%1 already exists in download directory. Overwrite?"), dest);
 			
 			if (!confirm (tr ("Overwrite?"), overwritemsg))
@@ -224,7 +227,7 @@
 		return;
 	
 	modifyDest (dest);
-	print ("DOWNLOAD: %1 -> %2\n", url, PartDownloader::getDownloadPath() + dest);
+	print ("DOWNLOAD: %1 -> %2\n", url, PartDownloader::getDownloadPath() + DIRSLASH + dest);
 	PartDownloadRequest* req = new PartDownloadRequest (url, dest, primary, this);
 	
 	m_filesToDownload << dest;
@@ -260,7 +263,7 @@
 		g_win->fullRefresh();
 		g_win->R()->resetAngles();
 	}
-		
+	
 	if (net_autoclose && !failed) {
 		// Close automatically if desired.
 		accept();
@@ -298,7 +301,7 @@
 	m_prompt (parent),
 	m_url (url),
 	m_dest (dest),
-	m_fpath (PartDownloader::getDownloadPath() + dest),
+	m_fpath (PartDownloader::getDownloadPath() + DIRSLASH + dest),
 	m_nam (new QNetworkAccessManager),
 	m_firstUpdate (true),
 	m_state (Requesting),
@@ -410,12 +413,9 @@
 	// from unknown file references, try resolve that by downloading the reference.
 	// This is why downloading a part may end up downloading multiple files, as
 	// it resolves dependencies.
-	for (LDObject* obj : *f) {
-		if (obj->getType() != LDObject::Error)
-			continue;
-		
-		LDErrorObject* err = static_cast<LDErrorObject*> (obj);
-		if (err->fileRef() == "")
+	for (LDObject* obj : f->objects()) {
+		LDError* err = dynamic_cast<LDError*> (obj);
+		if (!err || err->fileRef().isEmpty())
 			continue;
 		
 		str dest = err->fileRef();
@@ -423,8 +423,10 @@
 		m_prompt->downloadFile (dest, str (PartDownloader::k_UnofficialURL) + dest, false);
 	}
 	
-	if (m_primary)
+	if (m_primary) {
+		addRecentFile (m_fpath);
 		m_prompt->setPrimaryFile (f);
+	}
 	
 	m_prompt->checkIfFinished();
 }
--- a/src/download.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/download.h	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
--- a/src/extprogs.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/extprogs.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -31,13 +31,13 @@
 #include "file.h"
 #include "widgets.h"
 #include "history.h"
-#include "labeledwidget.h"
 #include "ui_ytruder.h"
 #include "ui_intersector.h"
 #include "ui_rectifier.h"
 #include "ui_coverer.h"
 #include "ui_isecalc.h"
 #include "ui_edger2.h"
+#include "dialogs.h"
 
 enum extprog {
 	Isecalc,
@@ -49,22 +49,32 @@
 };
 
 // =============================================================================
-cfg (str, prog_isecalc, "");
-cfg (str, prog_intersector, "");
-cfg (str, prog_coverer, "");
-cfg (str, prog_ytruder, "");
-cfg (str, prog_rectifier, "");
-cfg (str, prog_edger2, "");
+// -----------------------------------------------------------------------------
+cfg (String, prog_isecalc, "");
+cfg (String, prog_intersector, "");
+cfg (String, prog_coverer, "");
+cfg (String, prog_ytruder, "");
+cfg (String, prog_rectifier, "");
+cfg (String, prog_edger2, "");
+
+StringConfig* const g_extProgPaths[] = {
+	&prog_isecalc,
+	&prog_intersector,
+	&prog_coverer,
+	&prog_ytruder,
+	&prog_rectifier,
+	&prog_edger2,
+};
 
 #ifndef _WIN32
-cfg (bool, prog_isecalc_wine, false);
-cfg (bool, prog_intersector_wine, false);
-cfg (bool, prog_coverer_wine, false);
-cfg (bool, prog_ytruder_wine, false);
-cfg (bool, prog_rectifier_wine, false);
-cfg (bool, prog_edger2_wine, false);
+cfg (Bool, prog_isecalc_wine, false);
+cfg (Bool, prog_intersector_wine, false);
+cfg (Bool, prog_coverer_wine, false);
+cfg (Bool, prog_ytruder_wine, false);
+cfg (Bool, prog_rectifier_wine, false);
+cfg (Bool, prog_edger2_wine, false);
 
-boolconfig* const g_extProgWine[] = {
+BoolConfig* const g_extProgWine[] = {
 	&prog_isecalc_wine,
 	&prog_intersector_wine,
 	&prog_coverer_wine,
@@ -84,19 +94,24 @@
 };
 
 // =============================================================================
-static bool checkProgPath (str path, const extprog prog) {
+// -----------------------------------------------------------------------------
+static bool checkProgPath (const extprog prog) {
+	alias path = g_extProgPaths[prog]->value;
+	
 	if (path.length() > 0)
 		return true;
 	
-	const char* name = g_extProgNames[prog];
+	ExtProgPathPrompt* dlg = new ExtProgPathPrompt (g_extProgNames[prog]);
+	if (dlg->exec() && !dlg->getPath().isEmpty()) {
+		path = dlg->getPath();
+		return true;
+	}
 	
-	critical (fmt (QObject::tr ("Couldn't run %1 as no path has "
-		"been defined for it. Use the configuration dialog's External Programs "
-		"tab to define a path for %1."), name));
 	return false;
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 static str processErrorString (QProcess& proc) {
 	switch (proc.error()) {
 	case QProcess::FailedToStart:
@@ -120,6 +135,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 static bool mkTempFile (QTemporaryFile& tmp, str& fname) {
 	if (!tmp.open())
 		return false;
@@ -130,10 +146,12 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void writeObjects (List<LDObject*>& objects, File& f) {
 	for (LDObject* obj : objects) {
 		if (obj->getType() == LDObject::Subfile) {
-			List<LDObject*> objs = static_cast<LDSubfileObject*> (obj)->inlineContents (true, false);
+			LDSubfile* ref = static_cast<LDSubfile*> (obj);
+			List<LDObject*> objs = ref->inlineContents (LDSubfile::DeepInline);
 			
 			writeObjects (objs, f);
 			
@@ -144,6 +162,8 @@
 	}
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void writeObjects (List<LDObject*>& objects, str fname) {
 	// Write the input file
 	File f (fname, File::Write);
@@ -158,15 +178,17 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void writeSelection (str fname) {
 	writeObjects (g_win->sel(), fname);
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void writeColorGroup (const short colnum, str fname) {
 	List<LDObject*> objects;
 	
-	for (LDObject* obj : *LDFile::current()) {
+	for (LDObject* obj : LDFile::current()->objects()) {
 		if (obj->isColored() == false || obj->color() != colnum)
 			continue;
 		
@@ -177,6 +199,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 bool runUtilityProcess (extprog prog, str path, str argvstr) {
 	QTemporaryFile input, output;
 	str inputname, outputname;
@@ -231,7 +254,8 @@
 	return true;
 }
 
-// ================================================================================================
+// =============================================================================
+// -----------------------------------------------------------------------------
 static void insertOutput (str fname, bool replace, List<short> colorsToReplace) {
 #ifndef RELEASE
 	QFile::copy (fname, "./debug_lastOutput");
@@ -271,13 +295,12 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
 // Interface for Ytruder
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Ytruder, 0) {
 	setlocale (LC_ALL, "C");
 	
-	if (!checkProgPath (prog_ytruder, Ytruder))
+	if (!checkProgPath (Ytruder))
 		return;
 	
 	QDialog* dlg = new QDialog;
@@ -327,13 +350,12 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
 // Rectifier interface
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Rectifier, 0){
 	setlocale (LC_ALL, "C");
 	
-	if (!checkProgPath (prog_rectifier, Rectifier))
+	if (!checkProgPath (Rectifier))
 		return;
 	
 	QDialog* dlg = new QDialog;
@@ -370,20 +392,13 @@
 	insertOutput (outDATName, true, {});
 }
 
-LabeledWidget<QComboBox>* buildColorSelector (const char* label) {
-	LabeledWidget<QComboBox>* widget = new LabeledWidget<QComboBox> (label, new QComboBox);
-	makeColorSelector (widget->w());
-	return widget;
-}
-
-// =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 // Intersector interface
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Intersector, 0) {
 	setlocale (LC_ALL, "C");
 	
-	if (!checkProgPath (prog_intersector, Intersector))
+	if (!checkProgPath (Intersector))
 		return;
 	
 	QDialog* dlg = new QDialog;
@@ -461,20 +476,20 @@
 	if (repeatInverse && runUtilityProcess (Intersector, prog_intersector, argv_inverse))
 		insertOutput (outDAT2Name, false, {cutCol});
 	
-	if (ui.cb_edges->isChecked() && runUtilityProcess (Isecalc, prog_isecalc,
-		join ({inDATName, cutDATName, edgesDATName})))
-	{
+	if (
+		ui.cb_edges->isChecked() &&
+		checkProgPath (Isecalc) &&
+		runUtilityProcess (Isecalc, prog_isecalc, join ({inDATName, cutDATName, edgesDATName}))
+	)
 		insertOutput (edgesDATName, false, {});
-	}
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Coverer, 0) {
 	setlocale (LC_ALL, "C");
 	
-	if (!checkProgPath (prog_coverer, Coverer))
+	if (!checkProgPath (Coverer))
 		return;
 	
 	QDialog* dlg = new QDialog;
@@ -525,12 +540,11 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Isecalc, 0) {
 	setlocale (LC_ALL, "C");
 	
-	if (!checkProgPath (prog_isecalc, Isecalc))
+	if (!checkProgPath (Isecalc))
 		return;
 	
 	Ui::IsecalcUI ui;
@@ -577,12 +591,11 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Edger2, 0) {
 	setlocale (LC_ALL, "C");
 	
-	if (!checkProgPath (prog_edger2, Edger2))
+	if (!checkProgPath (Edger2))
 		return;
 	
 	QDialog* dlg = new QDialog;
--- a/src/file.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/file.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -14,14 +14,17 @@
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *  =====================================================================
+ *
+ *  file.cpp: File I/O and management.
+ *  - File loading, parsing, manipulation, saving, closing.
+ *  - LDraw path verification.
  */
 
 #include <QMessageBox>
 #include <QFileDialog>
 #include <QDir>
 #include <QApplication>
-
-#include <stdlib.h>
 #include "common.h"
 #include "config.h"
 #include "file.h"
@@ -31,19 +34,23 @@
 #include "dialogs.h"
 #include "gldraw.h"
 
-cfg (str, io_ldpath, "");
-cfg (str, io_recentfiles, "");
-extern_cfg (str, net_downloadpath);
+cfg (String, io_ldpath, "");
+cfg (List, io_recentfiles, {});
+extern_cfg (String, net_downloadpath);
+extern_cfg (Bool, gl_logostuds);
 
 static bool g_loadingMainFile = false;
 static const int g_MaxRecentFiles = 5;
 static bool g_aborted = false;
+static LDFile* g_logoedStud = null;
+static LDFile* g_logoedStud2 = null;
 
 LDFile* LDFile::m_curfile = null;
 
 DEFINE_PROPERTY (QListWidgetItem*, LDFile, listItem, setListItem)
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 namespace LDPaths {
 	static str pathError;
 	
@@ -94,6 +101,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 LDFile::LDFile() {
 	setImplicit (true);
 	setSavePos (-1);
@@ -102,13 +110,14 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 LDFile::~LDFile() {
 	// Clear everything from the model
-	for (LDObject* obj : m_objs)
+	for (LDObject* obj : objects())
 		delete obj;
 	
 	// Clear the cache as well
-	for (LDObject* obj : m_cache)
+	for (LDObject* obj : cache())
 		delete obj;
 	
 	// Remove this file from the list of files
@@ -123,25 +132,33 @@
 	// file as something else.
 	if (this == LDFile::current()) {
 		// If we closed the last file, create a blank one.
-		if (g_loadedFiles.size() == 0)
+		if (countExplicitFiles() == 0)
 			newFile();
-		else
-			LDFile::setCurrent (g_loadedFiles[0]);
+		else {
+			// Find the first explicit file loaded
+			int idx = 0;
+			while (g_loadedFiles[idx]->implicit())
+				idx++;
+			
+			LDFile::setCurrent (g_loadedFiles[idx]);
+		}
 	}
 	
 	g_win->updateFileList();
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 LDFile* findLoadedFile (str name) {
 	for (LDFile* file : g_loadedFiles)
-		if (file->name() == name)
+		if (!file->name().isEmpty() && file->getShortName() == name)
 			return file;
 	
 	return null;
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 str dirname (str path) {
 	long lastpos = path.lastIndexOf (DIRSLASH);
 	
@@ -157,6 +174,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 str basename (str path) {
 	long lastpos = path.lastIndexOf (DIRSLASH);
 	
@@ -167,6 +185,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 File* openLDrawFile (str relpath, bool subdirs) {
 	print ("%1: Try to open %2\n", __func__, relpath);
 	File* f = new File;
@@ -213,8 +232,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void FileLoader::start() {
 	setDone (false);
 	setProgress (0);
@@ -223,10 +241,10 @@
 	if (concurrent()) {
 		g_aborted = false;
 		
-		// Show a progress dialog if we're loading the main file here and move
-		// the actual work to a separate thread as this can be a rather intensive
-		// operation and if we don't respond quickly enough, the program can be
-		// deemed inresponsive.. which is a bad thing.
+		// Show a progress dialog if we're loading the main file here so we can
+		// show progress updates and keep the WM posted that we're still here.
+		// Of course we cannot exec() the dialog because then the dialog would
+		// block.
 		dlg = new OpenProgressDialog (g_win);
 		dlg->setNumLines (lines().size());
 		dlg->setModal (true);
@@ -238,12 +256,15 @@
 	} else
 		dlg = null;
 	
+	// Begin working
 	work (0);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void FileLoader::work (int i) {
+	// User wishes to abort, so stop here now.
 	if (aborted()) {
-		// We were flagged for abortion, so abort.
 		for (LDObject* obj : m_objs)
 			delete obj;
 		
@@ -252,6 +273,7 @@
 		return;
 	}
 	
+	// Parse up to 300 lines per iteration
 	int max = i + 300;
 	
 	for (; i < max && i < (int) lines().size(); ++i) {
@@ -267,7 +289,7 @@
 		
 		// Check for parse errors and warn about tthem
 		if (obj->getType() == LDObject::Error) {
-			log ("Couldn't parse line #%1: %2", m_progress + 1, static_cast<LDErrorObject*> (obj)->reason);
+			log ("Couldn't parse line #%1: %2", m_progress + 1, static_cast<LDError*> (obj)->reason);
 			
 			if (m_warningsPointer)
 				(*m_warningsPointer)++;
@@ -276,24 +298,39 @@
 		m_objs << obj;
 		setProgress (i);
 		
+		// If we have a dialog pointer, update the progress now
 		if (concurrent())
 			dlg->updateProgress (i);
 	}
 	
+	// If we're done now, tell the environment we're done and stop.
 	if (i >= ((int) lines().size()) - 1) {
 		emit workDone();
 		setDone (true);
 		return;
 	}
 	
+	// Otherwise, continue, by recursing back.
 	if (!done()) {
+		// If we have a dialog to show progress output to, we cannot just call
+		// work() again immediately as the dialog needs some processor cycles as
+		// well. Thus, take a detour through the event loop by using the
+		// meta-object system.
+		//
+		// This terminates the loop here and control goes back to the function
+		// which called the file loader. It will keep processing the event loop
+		// until we're ready (see loadFileContents), thus the event loop will
+		// eventually catch the invokation we throw here and send us back. Though
+		// it's not technically recursion anymore, more like a for loop. :P
 		if (concurrent())
-			QMetaObject::invokeMethod (this, "work", Qt::QueuedConnection, Q_ARG (ulong, i + 1));
+			QMetaObject::invokeMethod (this, "work", Qt::QueuedConnection, Q_ARG (int, i + 1));
 		else
 			work (i + 1);
 	}
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void FileLoader::abort() {
 	setAborted (true);
 	
@@ -302,8 +339,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 List<LDObject*> loadFileContents (File* f, ulong* numWarnings, bool* ok) {
 	List<str> lines;
 	List<LDObject*> objs;
@@ -323,6 +359,10 @@
 	loader->setConcurrent (g_loadingMainFile);
 	loader->start();
 	
+	// After start() returns, if the loader isn't done yet, it's delaying
+	// its next iteration through the event loop. We need to catch this here
+	// by telling the event loop to tick, which will tick the file loader again.
+	// We keep doing this until the file loader is ready.
 	while (loader->done() == false)
 		qApp->processEvents();
 	
@@ -335,8 +375,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 LDFile* openDATFile (str path, bool search) {
 	// Convert the file name to lowercase since some parts contain uppercase
 	// file names. I'll assume here that the library will always use lowercase
@@ -383,8 +422,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 bool LDFile::safeToClose() {
 	typedef QMessageBox msgbox;
 	setlocale (LC_ALL, "C");
@@ -433,8 +471,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void closeAll() {
 	// Remove all loaded files and the objects they contain
 	List<LDFile*> files = g_loadedFiles;
@@ -443,8 +480,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void newFile() {
 	// Create a new anonymous file and set it to our current
 	LDFile* f = new LDFile;
@@ -462,10 +498,9 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void addRecentFile (str path) {
-	QStringList rfiles = io_recentfiles.value.split ('@');
+	alias rfiles = io_recentfiles.value;
 	int idx = rfiles.indexOf (path);
 	
 	// If this file already is in the list, pop it out.
@@ -484,17 +519,13 @@
 	// Add the file
 	rfiles << path;
 	
-	// Rebuild the config string
-	io_recentfiles = rfiles.join ("@");
-	
-	config::save();
+	Config::save();
 	g_win->updateRecentFilesMenu();
 }
 
 // =============================================================================
+// Open an LDraw file and set it as the main model
 // -----------------------------------------------------------------------------
-// Open an LDraw file and set it as the main model
-// =============================================================================
 void openMainFile (str path) {
 	g_loadingMainFile = true;
 	LDFile* file = openDATFile (path, false);
@@ -530,8 +561,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 bool LDFile::save (str savepath) {
 	if (!savepath.length())
 		savepath = name();
@@ -544,11 +574,11 @@
 	// If the second object in the list holds the file name, update that now.
 	// Only do this if the file is explicitly open. If it's saved into a directory
 	// called "s" or "48", prepend that into the name.
-	LDCommentObject* fpathComment = null;
+	LDComment* fpathComment = null;
 	LDObject* first = object (1);
 	
 	if (!implicit() && first != null && first->getType() == LDObject::Comment) {
-		fpathComment = static_cast<LDCommentObject*> (first);
+		fpathComment = static_cast<LDComment*> (first);
 		
 		if (fpathComment->text.left (6) == "Name: ") {
 			str newname;
@@ -565,7 +595,7 @@
 	
 	// File is open, now save the model to it. Note that LDraw requires files to
 	// have DOS line endings, so we terminate the lines with \r\n.
-	for (LDObject* obj : objs())
+	for (LDObject* obj : objects())
 		f.write (obj->raw() + "\r\n");
 	
 	// File is saved, now clean up.
@@ -581,17 +611,18 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 #define CHECK_TOKEN_COUNT(N) \
 	if (tokens.size() != N) \
-		return new LDErrorObject (line, "Bad amount of tokens");
+		return new LDError (line, "Bad amount of tokens");
 
 #define CHECK_TOKEN_NUMBERS(MIN, MAX) \
 	for (ushort i = MIN; i <= MAX; ++i) \
 		if (!isNumber (tokens[i])) \
-			return new LDErrorObject (line, fmt ("Token #%1 was `%2`, expected a number", (i + 1), tokens[i]));
+			return new LDError (line, fmt ("Token #%1 was `%2`, expected a number", (i + 1), tokens[i]));
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 static vertex parseVertex (QStringList& s, const ushort n) {
 	vertex v;
 	
@@ -602,21 +633,20 @@
 }
 
 // =============================================================================
-// -----------------------------------------------------------------------------
 // This is the LDraw code parser function. It takes in a string containing LDraw
 // code and returns the object parsed from it. parseLine never returns null,
 // the object will be LDError if it could not be parsed properly.
-// =============================================================================
+// -----------------------------------------------------------------------------
 LDObject* parseLine (str line) {
 	QStringList tokens = line.split (" ", str::SkipEmptyParts);
 	
 	if (tokens.size() <= 0) {
 		// Line was empty, or only consisted of whitespace
-		return new LDEmptyObject;
+		return new LDEmpty;
 	}
 	
 	if (tokens[0].length() != 1 || tokens[0][0].isDigit() == false)
-		return new LDErrorObject (line, "Illogical line code");
+		return new LDError (line, "Illogical line code");
 	
 	int num = tokens[0][0].digitValue();
 	
@@ -631,25 +661,25 @@
 		
 		// Handle BFC statements
 		if (tokens.size() > 2 && tokens[1] == "BFC") {
-			for (short i = 0; i < LDBFCObject::NumStatements; ++i)
-				if (comm == fmt ("BFC %1", LDBFCObject::statements [i]))
-					return new LDBFCObject ((LDBFCObject::Type) i);
+			for (short i = 0; i < LDBFC::NumStatements; ++i)
+				if (comm == fmt ("BFC %1", LDBFC::statements [i]))
+					return new LDBFC ((LDBFC::Type) i);
 			
 			// MLCAD is notorious for stuffing these statements in parts it
 			// creates. The above block only handles valid statements, so we
 			// need to handle MLCAD-style invertnext, clip and noclip separately.
 			struct {
 				const char* a;
-				LDBFCObject::Type b;
+				LDBFC::Type b;
 			} BFCData[] = {
-				{ "INVERTNEXT", LDBFCObject::InvertNext },
-				{ "NOCLIP", LDBFCObject::NoClip },
-				{ "CLIP", LDBFCObject::Clip }
+				{ "INVERTNEXT", LDBFC::InvertNext },
+				{ "NOCLIP", LDBFC::NoClip },
+				{ "CLIP", LDBFC::Clip }
 			};
 			
 			for (const auto& i : BFCData)
 				if (comm == fmt ("BFC CERTIFY %1", i.a))
-					return new LDBFCObject (i.b);
+					return new LDBFC (i.b);
 		}
 		
 		if (tokens.size() > 2 && tokens[1] == "!LDFORGE") {
@@ -659,7 +689,7 @@
 				CHECK_TOKEN_COUNT (7)
 				CHECK_TOKEN_NUMBERS (3, 6)
 				
-				LDVertexObject* obj = new LDVertexObject;
+				LDVertex* obj = new LDVertex;
 				obj->setColor (tokens[3].toLong());
 				
 				for (const Axis ax : g_Axes)
@@ -670,7 +700,7 @@
 				CHECK_TOKEN_COUNT (9);
 				CHECK_TOKEN_NUMBERS (5, 8)
 				
-				LDOverlayObject* obj = new LDOverlayObject;
+				LDOverlay* obj = new LDOverlay;
 				obj->setFilename (tokens[3]);
 				obj->setCamera (tokens[4].toLong());
 				obj->setX (tokens[5].toLong());
@@ -682,7 +712,7 @@
 		}
 		
 		// Just a regular comment:
-		LDCommentObject* obj = new LDCommentObject;
+		LDComment* obj = new LDComment;
 		obj->text = comm;
 		return obj;
 	}
@@ -701,12 +731,12 @@
 		
 		// If we cannot open the file, mark it an error
 		if (!load) {
-			LDErrorObject* obj = new LDErrorObject (line, fmt ("Could not open %1", tokens[14]));
+			LDError* obj = new LDError (line, fmt ("Could not open %1", tokens[14]));
 			obj->setFileRef (tokens[14]);
 			return obj;
 		}
 		
-		LDSubfileObject* obj = new LDSubfileObject;
+		LDSubfile* obj = new LDSubfile;
 		obj->setColor (tokens[1].toLong());
 		obj->setPosition (parseVertex (tokens, 2));  // 2 - 4
 		
@@ -725,7 +755,7 @@
 		CHECK_TOKEN_NUMBERS (1, 7)
 		
 		// Line
-		LDLineObject* obj = new LDLineObject;
+		LDLine* obj = new LDLine;
 		obj->setColor (tokens[1].toLong());
 		
 		for (short i = 0; i < 2; ++i)
@@ -739,7 +769,7 @@
 		CHECK_TOKEN_NUMBERS (1, 10)
 		
 		// Triangle
-		LDTriangleObject* obj = new LDTriangleObject;
+		LDTriangle* obj = new LDTriangle;
 		obj->setColor (tokens[1].toLong());
 		
 		for (short i = 0; i < 3; ++i)
@@ -757,9 +787,9 @@
 		LDObject* obj;
 		
 		if (num == 4)
-			obj = new LDQuadObject;
+			obj = new LDQuad;
 		else
-			obj = new LDCondLineObject;
+			obj = new LDCndLine;
 		
 		obj->setColor (tokens[1].toLong());
 		
@@ -770,13 +800,12 @@
 	}
 	
 	default: // Strange line we couldn't parse
-		return new LDErrorObject (line, "Unknown line code number");
+		return new LDError (line, "Unknown line code number");
 	}
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 LDFile* getFile (str filename) {
 	// Try find the file in the list of loaded files
 	LDFile* load = findLoadedFile (filename);
@@ -789,8 +818,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void reloadAllSubfiles() {
 	if (!LDFile::current())
 		return;
@@ -799,21 +827,21 @@
 	g_loadedFiles << LDFile::current();
 	
 	// Go through all objects in the current file and reload the subfiles
-	for (LDObject* obj : LDFile::current()->objs()) {
+	for (LDObject* obj : LDFile::current()->objects()) {
 		if (obj->getType() == LDObject::Subfile) {
-			LDSubfileObject* ref = static_cast<LDSubfileObject*> (obj);
+			LDSubfile* ref = static_cast<LDSubfile*> (obj);
 			LDFile* fileInfo = getFile (ref->fileInfo()->name());
 			
 			if (fileInfo)
 				ref->setFileInfo (fileInfo);
 			else
-				ref->replace (new LDErrorObject (ref->raw(), "Could not open referred file"));
+				ref->replace (new LDError (ref->raw(), "Could not open referred file"));
 		}
 		
 		// Reparse gibberish files. It could be that they are invalid because
 		// of loading errors. Circumstances may be different now.
 		if (obj->getType() == LDObject::Error)
-			obj->replace (parseLine (static_cast<LDErrorObject*> (obj)->contents));
+			obj->replace (parseLine (static_cast<LDError*> (obj)->contents));
 	}
 	
 	// Close all files left unused
@@ -821,11 +849,10 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 ulong LDFile::addObject (LDObject* obj) {
-	m_history.add (new AddHistory (m_objs.size(), obj));
-	m_objs << obj;
+	m_history.add (new AddHistory (objects().size(), obj));
+	m_objects << obj;
 	
 	if (obj->getType() == LDObject::Vertex)
 		m_vertices << obj;
@@ -835,25 +862,32 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// -----------------------------------------------------------------------------
+void LDFile::addObjects (const List<LDObject*> objs) {
+	for (LDObject* obj : objs)
+		if (obj)
+			addObject (obj);
+}
+
 // =============================================================================
+// -----------------------------------------------------------------------------
 void LDFile::insertObj (const ulong pos, LDObject* obj) {
 	m_history.add (new AddHistory (pos, obj));
-	m_objs.insert (pos, obj);
+	m_objects.insert (pos, obj);
 	obj->setFile (this);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void LDFile::forgetObject (LDObject* obj) {
 	ulong idx = obj->getIndex();
 	m_history.add (new DelHistory (idx, obj));
-	m_objs.erase (idx);
+	m_objects.erase (idx);
 	obj->setFile (null);
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 bool safeToCloseAll() {
 	for (LDFile* f : g_loadedFiles)
 		if (!f->safeToClose())
@@ -863,6 +897,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void LDFile::setObject (ulong idx, LDObject* obj) {
 	assert (idx < numObjs());
 	
@@ -872,17 +907,19 @@
 	m_history << new EditHistory (idx, oldcode, newcode);
 	
 	obj->setFile (this);
-	m_objs[idx] = obj;
+	m_objects[idx] = obj;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 static List<LDFile*> getFilesUsed (LDFile* node) {
 	List<LDFile*> filesUsed;
 	
-	for (LDObject* obj : *node) {
+	for (LDObject* obj : node->objects()) {
 		if (obj->getType() != LDObject::Subfile)
 			continue;
 		
-		LDSubfileObject* ref = static_cast<LDSubfileObject*> (obj);
+		LDSubfile* ref = static_cast<LDSubfile*> (obj);
 		filesUsed << ref->fileInfo();
 		filesUsed << getFilesUsed (ref->fileInfo());
 	}
@@ -892,6 +929,7 @@
 
 // =============================================================================
 // Find out which files are unused and close them.
+// -----------------------------------------------------------------------------
 void LDFile::closeUnused() {
 	List<LDFile*> filesUsed = getFilesUsed (LDFile::current());
 	
@@ -922,32 +960,35 @@
 	g_loadedFiles << filesUsed;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 LDObject* LDFile::object (ulong pos) const {
-	if (m_objs.size() <= pos)
+	if (m_objects.size() <= pos)
 		return null;
 	
-	return m_objs[pos];
+	return m_objects[pos];
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 LDObject* LDFile::obj (ulong pos) const {
 	return object (pos);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 ulong LDFile::numObjs() const {
-	return m_objs.size();
+	return objects().size();
 }
 
-LDFile& LDFile::operator<< (List<LDObject*> objs) {
-	for (LDObject* obj : objs)
-		addObject (obj);
-	
-	return *this;
-}
-
+// =============================================================================
+// -----------------------------------------------------------------------------
 bool LDFile::hasUnsavedChanges() const {
 	return !implicit() && history().pos() != savePos();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 str LDFile::getShortName() {
 	if (name().length() > 0)
 		return basename (name());
@@ -956,19 +997,83 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
+List<LDObject*> LDFile::inlineContents (LDSubfile::InlineFlags flags) {
+	// Possibly substitute with logoed studs:
+	// stud.dat -> stud-logo.dat
+	// stud2.dat -> stud-logo2.dat
+	if (gl_logostuds && (flags & LDSubfile::RendererInline)) {
+		if (name() == "stud.dat" && g_logoedStud)
+			return g_logoedStud->inlineContents (flags);
+		elif (name() == "stud2.dat" && g_logoedStud2)
+			return g_logoedStud2->inlineContents (flags);
+	}
+	
+	List<LDObject*> objs, objcache;
+	
+	bool deep = flags & LDSubfile::DeepInline,
+		doCache = flags & LDSubfile::CacheInline;
+	
+	// If we have this cached, just clone that
+	if (deep && cache().size()) {
+		for (LDObject* obj : cache())
+			objs << obj->clone();
+	} else {
+		if (!deep)
+			doCache = false;
+		
+		for (LDObject* obj : objects()) {
+			// Skip those without scemantic meaning
+			if (!obj->isScemantic())
+				continue;
+			
+			// Got another sub-file reference, inline it if we're deep-inlining. If not,
+			// just add it into the objects normally. Also, we only cache immediate
+			// subfiles and this is not one. Yay, recursion!
+			if (deep && obj->getType() == LDObject::Subfile) {
+				LDSubfile* ref = static_cast<LDSubfile*> (obj);
+				
+				// We only want to cache immediate subfiles, so shed the caching
+				// flag when recursing deeper in hierarchy.
+				List<LDObject*> otherobjs = ref->inlineContents (flags & ~(LDSubfile::CacheInline));
+				
+				for (LDObject* otherobj : otherobjs) {
+					// Cache this object, if desired
+					if (doCache)
+						objcache << otherobj->clone();
+					
+					objs << otherobj;
+				}
+			} else {
+				if (doCache)
+					objcache << obj->clone();
+				
+				objs << obj->clone();
+			}
+		}
+		
+		if (doCache)
+			setCache (objcache);
+	}
+	
+	return objs;
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
 LDFile* LDFile::current() {
 	return m_curfile;
 }
 
 // =============================================================================
-/* Sets the given file as the current one on display. At some point in time this
- * was an operation completely unheard of. ;)
- *
- * FIXME: f can be temporarily null. This probably should not be the case.
- */
+// Sets the given file as the current one on display. At some point in time this
+// was an operation completely unheard of. ;)
+//
+// FIXME: f can be temporarily null. This probably should not be the case.
+// -----------------------------------------------------------------------------
 void LDFile::setCurrent (LDFile* f) {
-	/* Implicit files were loaded for caching purposes and must never be set
-	 * current. */
+	// Implicit files were loaded for caching purposes and must never be set
+	// current.
 	if (f && f->implicit())
 		return;
 	
@@ -989,6 +1094,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 int LDFile::countExplicitFiles() {
 	int count = 0;
 	
@@ -1002,12 +1108,24 @@
 // =============================================================================
 // This little beauty closes the initial file that was open at first when opening
 // a new file over it.
+// -----------------------------------------------------------------------------
 void LDFile::closeInitialFile() {
-	if (countExplicitFiles() == 2 &&
+	if (
+		countExplicitFiles() == 2 &&
 		g_loadedFiles[0]->name() == "" &&
-		!g_loadedFiles[0]->hasUnsavedChanges()) {
+		!g_loadedFiles[0]->hasUnsavedChanges()
+	)
 		delete g_loadedFiles[0];
-	}
 }
 
-#include "build/moc_file.cpp"
\ No newline at end of file
+// =============================================================================
+// -----------------------------------------------------------------------------
+void loadLogoedStuds() {
+	print ("Loading logoed studs...\n");
+	
+	delete g_logoedStud;
+	delete g_logoedStud2;
+	
+	g_logoedStud = openDATFile ("stud-logo.dat", true);
+	g_logoedStud2 = openDATFile ("stud2-logo.dat", true);
+}
\ No newline at end of file
--- a/src/file.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/file.h	Sat Sep 07 13:23:09 2013 +0300
@@ -50,7 +50,7 @@
 // =============================================================================
 class LDFile : public QObject {
 	Q_OBJECT
-	READ_PROPERTY (List<LDObject*>, objs, setObjects)
+	READ_PROPERTY (List<LDObject*>, objects, setObjects)
 	READ_PROPERTY (History, history, setHistory)
 	READ_PROPERTY (List<LDObject*>, vertices, setVertices)
 	PROPERTY (str, name, setName)
@@ -67,8 +67,11 @@
 	~LDFile();
 	
 	ulong addObject (LDObject* obj);                 // Adds an object to this file at the end of the file.
+	void addObjects (const List<LDObject*> objs);
 	void forgetObject (LDObject* obj);               // Deletes the given object from the object chain.
+	str getShortName();
 	bool hasUnsavedChanges() const;                  // Does this file have unsaved changes?
+	List<LDObject*> inlineContents (LDSubfile::InlineFlags flags);
 	void insertObj (const ulong pos, LDObject* obj);
 	ulong numObjs() const;
 	LDObject* object (ulong pos) const;
@@ -77,59 +80,19 @@
 	bool safeToClose();                              // Perform safety checks. Do this before closing any files!
 	void setObject (ulong idx, LDObject* obj);
 
-	LDFile& operator<< (LDObject* obj) {
-		addObject (obj);
-		return *this;
-	}
-	
-	LDFile& operator<< (List<LDObject*> objs);
-	
-	it begin() {
-		return PROP_NAME (objs).begin();
-	}
-	
-	c_it begin() const {
-		return PROP_NAME (objs).begin();
-	}
-	
-	it end() {
-		return PROP_NAME (objs).end();
-	}
-	
-	c_it end() const {
-		return PROP_NAME (objs).end();
-	}
-	
-	void openHistory() {
-		m_history.open();
-	}
-	
-	void closeHistory() {
-		m_history.close();
-	}
-	
-	void undo() {
-		m_history.undo();
-	}
-	
-	void redo() {
-		m_history.redo();
-	}
-	
-	void clearHistory() {
-		m_history.clear();
-	}
-	
-	void addToHistory (AbstractHistoryEntry* entry) {
-		m_history << entry;
-	}
+	inline LDFile& operator<< (LDObject* obj) { addObject (obj); return *this; }
+	inline void openHistory() { m_history.open(); }
+	inline void closeHistory() { m_history.close(); }
+	inline void undo() { m_history.undo(); }
+	inline void redo() { m_history.redo(); }
+	inline void clearHistory() { m_history.clear(); }
+	inline void addToHistory (AbstractHistoryEntry* entry) { m_history << entry; }
 	
 	static void closeUnused();
 	static LDFile* current();
 	static void setCurrent (LDFile* f);
 	static void closeInitialFile();
 	static int countExplicitFiles();
-	str getShortName();
 	
 private:
 	static LDFile* m_curfile;
@@ -171,6 +134,7 @@
 extern List<LDFile*> g_loadedFiles;
 
 void addRecentFile (str path);
+void loadLogoedStuds();
 str basename (str path);
 str dirname (str path);
 
--- a/src/gldata.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/gldata.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -5,7 +5,8 @@
 #include "misc.h"
 #include "gldraw.h"
 
-cfg (bool, gl_blackedges, true);
+cfg (Bool, gl_blackedges, false);
+static List<short> g_warnedColors;
 
 // =============================================================================
 // -----------------------------------------------------------------------------
@@ -89,9 +90,7 @@
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-void VertexCompiler::Array::merge (
-	Array* other
-) {
+void VertexCompiler::Array::merge (Array* other) {
 	// Ensure there's room for both buffers
 	resizeToFit (writtenSize() + other->writtenSize());
 	
@@ -101,94 +100,69 @@
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-VertexCompiler::VertexCompiler() : m_file (null) {
-	for (int i = 0; i < NumArrays; ++i) {
-		m_mainArrays[i] = new Array;
-		m_changed[i] = false;
-	}
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-VertexCompiler::~VertexCompiler() {
-	for (Array* array : m_mainArrays)
-		delete array;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void VertexCompiler::compileVertex (::vertex v, QColor col, Array* array) {
-	VertexCompiler::Vertex glvertex;
-	glvertex.x = v.x();
-	glvertex.y = -v.y();
-	glvertex.z = v.z();
-	glvertex.color =
-		(col.red()   & 0xFF) << 0x00 |
-		(col.green() & 0xFF) << 0x08 |
-		(col.blue()  & 0xFF) << 0x10 |
-		(col.alpha() & 0xFF) << 0x18;
-	
-	array->write (glvertex);
+VertexCompiler::VertexCompiler() :
+	m_file (null)
+{
+	memset (m_changed, 0xFF, sizeof m_changed);
 }
 
+VertexCompiler::~VertexCompiler() {}
+
 // =============================================================================
+// Note: we use the top level object's color but the draw object's vertices.
+// This is so that the index color is generated correctly - it has to reference
+// the top level object's ID. This is crucial for picking to work.
 // -----------------------------------------------------------------------------
-// Note: we use the true object's color but the draw object's vertices. This is
-// so that the index color is generated correctly - it has to reference the true
-// object's ID. This is crucial for picking to work.
 void VertexCompiler::compilePolygon (LDObject* drawobj, LDObject* trueobj) {
-	Array** arrays = m_objArrays[trueobj];
-	LDObject::Type objtype = drawobj->getType();
-	const bool isline = (objtype == LDObject::Line || objtype == LDObject::CondLine);
-	const int verts = isline ? 2 : drawobj->vertices();
+	List<CompiledTriangle>& data = m_objArrays[trueobj];
 	
 	QColor normalColor = getObjectColor (trueobj, Normal),
-		pickColor = getObjectColor (trueobj, PickColor);
+	       pickColor = getObjectColor (trueobj, PickColor);
+	
+	LDObject::Type type = drawobj->getType();
+	assert (type != LDObject::Subfile);
 	
-	for (int i = 0; i < verts; ++i) {
-		compileVertex (drawobj->getVertex (i), normalColor, arrays[isline ? EdgeArray : MainArray]);
-		compileVertex (drawobj->getVertex (i), pickColor, arrays[isline ? EdgePickArray : PickArray]);
-	}
+	List<LDObject*> objs;
+	if (type == LDObject::Quad) {
+		for (LDTriangle* t : static_cast<LDQuad*> (drawobj)->splitToTriangles())
+			objs << t;
+	} else
+		objs << drawobj;
 	
-	// For non-lines, compile BFC data. Note that the front objects are what get
-	// reversed here! This is because we invert the Y axis, which inverts the
-	// entire part model, so we remedy that here.
-	if (!isline) {
-		QColor col = getObjectColor (trueobj, BFCBack);
-		for (int i = 0; i < verts; ++i)
-			compileVertex (drawobj->getVertex(i), col, arrays[BFCArray]);
+	for (LDObject* obj : objs) {
+		const LDObject::Type objtype = obj->getType();
+		const bool isline = (objtype == LDObject::Line || objtype == LDObject::CndLine);
+		const int verts = isline ? 2 : obj->vertices();
 		
-		col = getObjectColor (trueobj, BFCFront);
-		for (int i = verts - 1; i >= 0; --i)
-			compileVertex (drawobj->getVertex(i), col, arrays[BFCArray]);
+		CompiledTriangle a;
+		a.rgb = normalColor.rgb();
+		a.pickrgb = pickColor.rgb();
+		a.numVerts = verts;
+		a.obj = trueobj;
+		
+		for (int i = 0; i < verts; ++i) {
+			a.verts[i] = obj->getVertex (i);
+			a.verts[i].y() = -a.verts[i].y();
+		}
+		
+		data << a;
 	}
 }
 
 // =============================================================================
 // -----------------------------------------------------------------------------
 void VertexCompiler::compileObject (LDObject* obj, LDObject* topobj) {
-	if (m_objArrays.find (obj) == m_objArrays.end()) {
-		m_objArrays[obj] = new Array*[NumArrays];
-		
-		for (int i = 0; i < NumArrays; ++i)
-			m_objArrays[obj][i] = new Array;
-	} else {
-		for (int i = 0; i < NumArrays; ++i)
-			m_objArrays[obj][i]->clear();
-	}
-	
+	print ("compile %1 (%2)\n", obj->id(), topobj->id());
 	List<LDObject*> objs;
 	
 	switch (obj->getType()) {
 	case LDObject::Triangle:
 		compilePolygon (obj, topobj);
-		m_changed[MainArray] = true;
 		break;
 	
 	case LDObject::Quad:
-		for (LDTriangleObject* triangle : static_cast<LDQuadObject*> (obj)->splitToTriangles())
+		for (LDTriangle* triangle : static_cast<LDQuad*> (obj)->splitToTriangles())
 			compilePolygon (triangle, topobj);
-		m_changed[MainArray] = true;
 		break;
 	
 	case LDObject::Line:
@@ -196,7 +170,7 @@
 		break;
 	
 	case LDObject::Subfile:
-		objs = static_cast<LDSubfileObject*> (obj)->inlineContents (true, true);
+		objs = static_cast<LDSubfile*> (obj)->inlineContents (LDSubfile::RendererInline | LDSubfile::DeepCacheInline);
 		
 		for (LDObject* obj : objs) {
 			compileObject (obj, topobj);
@@ -207,22 +181,21 @@
 	default:
 		break;
 	}
+	
+	// Set all of m_changed to true
+	memset (m_changed, 0xFF, sizeof m_changed);
 }
 
 // =============================================================================
 // -----------------------------------------------------------------------------
 void VertexCompiler::compileFile() {
-	for (LDObject* obj : *m_file)
+	for (LDObject* obj : m_file->objects())
 		compileObject (obj, obj);
 }
 
 // =============================================================================
 // -----------------------------------------------------------------------------
 void VertexCompiler::forgetObject (LDObject* obj) {
-	for (int i = 0; i < NumArrays; ++i)
-		delete m_objArrays[obj][i];
-	
-	delete m_objArrays[obj];
 	m_objArrays.remove (obj);
 }
 
@@ -234,27 +207,111 @@
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-VertexCompiler::Array* VertexCompiler::getMergedBuffer (ArrayType type) {
+const VertexCompiler::Array* VertexCompiler::getMergedBuffer (ArrayType type) {
 	assert (type < NumArrays);
 	
-	if (m_changed) {
+	if (m_changed[type]) {
 		m_changed[type] = false;
-		m_mainArrays[type]->clear();
+		m_mainArrays[type].clear();
+		
+		print ("merge array %1\n", (int) type);
 		
-		for (LDObject* obj : *m_file) {
+		for (LDObject* obj : m_file->objects()) {
+			if (!obj->isScemantic())
+				continue;
+			
+			const LDObject::Type objtype = obj->getType();
+			const bool isline = (objtype == LDObject::Line || objtype == LDObject::CndLine);
+			const bool islinearray = (type == EdgeArray || type == EdgePickArray);
+			
+			if ((isline && !islinearray) || (!isline && islinearray))
+				continue;
+			
 			auto it = m_objArrays.find (obj);
 			
-			if (it != m_objArrays.end())
-				m_mainArrays[type]->merge ((*it)[type]);
+			if (it != m_objArrays.end()) {
+				const List<CompiledTriangle>& data = *it;
+				
+				for (const CompiledTriangle& i : data) {
+					Array* verts = postprocess (i, type);
+					m_mainArrays[type].merge (verts);
+					delete verts;
+				}
+			}
 		}
+		
+		print ("merged array: %1 bytes\n", m_mainArrays[type].writtenSize());
 	}
 	
-	return m_mainArrays[type];
+	return &m_mainArrays[type];
+}
+
+// =============================================================================
+// This turns a compiled triangle into usable VAO vertices
+// -----------------------------------------------------------------------------
+VertexCompiler::Array* VertexCompiler::postprocess (const CompiledTriangle& triangle, ArrayType type) {
+	Array* va = new Array;
+	List<Vertex> verts;
+	
+	for (int i = 0; i < triangle.numVerts; ++i) {
+		alias v0 = triangle.verts[i];
+		Vertex v;
+		v.x = v0.x();
+		v.y = v0.y();
+		v.z = v0.z();
+		
+		switch (type) {
+		case MainArray:
+		case EdgeArray:
+			v.color = triangle.rgb;
+			break;
+		
+		case PickArray:
+		case EdgePickArray:
+			v.color = triangle.pickrgb;
+		
+		case BFCArray:
+			break;
+		
+		case NumArrays:
+			assert (false);
+		}
+		
+		verts << v;
+	}
+	
+	if (type == BFCArray) {
+		int32 rgb = getObjectColor (triangle.obj, BFCFront).rgb();
+		for (Vertex v : verts) {
+			v.color = rgb;
+			va->write (v);
+		}
+		
+		rgb = getObjectColor (triangle.obj, BFCBack).rgb();
+		for (Vertex v : c_rev<Vertex> (verts)) {
+			v.color = rgb;
+			va->write (v);
+		}
+	} else {
+		for (Vertex v : verts)
+			va->write (v);
+	}
+	
+	return va;
 }
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-static List<short> g_warnedColors;
+uint32 VertexCompiler::getColorRGB (QColor& color) {
+	return
+		(color.red()   & 0xFF) << 0x00 |
+		(color.green() & 0xFF) << 0x08 |
+		(color.blue()  & 0xFF) << 0x10 |
+		(color.alpha() & 0xFF) << 0x18;
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
 QColor VertexCompiler::getObjectColor (LDObject* obj, ColorType colotype) const {
 	QColor qcol;
 	
@@ -280,7 +337,7 @@
 	
 	if ((colotype == BFCFront || colotype == BFCBack) &&
 		obj->getType() != LDObject::Line &&
-		obj->getType() != LDObject::CondLine) {
+		obj->getType() != LDObject::CndLine) {
 		
 		if (colotype == BFCFront)
 			qcol = QColor (40, 192, 0);
--- a/src/gldata.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/gldata.h	Sat Sep 07 13:23:09 2013 +0300
@@ -3,8 +3,10 @@
 
 #include "types.h"
 #include <QMap>
+#include <QRgb>
 
 class QColor;
+class LDTriangle;
 class LDFile;
 
 /* =============================================================================
@@ -52,6 +54,14 @@
 		NumArrays
 	};
 	
+	struct CompiledTriangle {
+		vertex    verts[3];
+		uint8     numVerts;
+		QRgb      rgb;
+		QRgb      pickrgb;
+		LDObject* obj;
+	};
+	
 	struct Vertex {
 		float x, y, z;
 		uint32 color;
@@ -87,16 +97,18 @@
 	void compileFile();
 	void compileObject (LDObject* obj, LDObject* topobj);
 	void forgetObject (LDObject* obj);
-	Array* getMergedBuffer (ArrayType type);
+	const Array* getMergedBuffer (ArrayType type);
 	QColor getObjectColor (LDObject* obj, ColorType list) const;
 	
+	static uint32 getColorRGB (QColor& color);
+	
 private:
 	void compilePolygon (LDObject* drawobj, LDObject* trueobj);
-	void compileVertex (vertex v, QColor col, VertexCompiler::Array* array);
+	Array* postprocess (const CompiledTriangle& i, ArrayType type);
 	
-	QMap<LDObject*, Array**> m_objArrays;
+	QMap<LDObject*, List<CompiledTriangle>> m_objArrays;
 	QMap<LDFile*, Array*> m_fileCache;
-	Array* m_mainArrays[NumArrays];
+	Array m_mainArrays[NumArrays];
 	LDFile* m_file;
 	bool m_changed[NumArrays];
 };
--- a/src/gldraw.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/gldraw.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -51,14 +51,15 @@
 	{{  0, -1, 0 }, Z, Y, false,  true },
 };
 
-cfg (str, gl_bgcolor, "#CCCCD9");
-cfg (str, gl_maincolor, "#707078");
-cfg (float, gl_maincolor_alpha, 1.0);
-cfg (int, gl_linethickness, 2);
-cfg (bool, gl_colorbfc, true);
-cfg (int, gl_camera, GLRenderer::Free);
-cfg (bool, gl_axes, false);
-cfg (bool, gl_wireframe, false);
+cfg (String, gl_bgcolor, "#CCCCD9");
+cfg (String, gl_maincolor, "#707078");
+cfg (Float, gl_maincolor_alpha, 1.0);
+cfg (Int, gl_linethickness, 2);
+cfg (Bool, gl_colorbfc, false);
+cfg (Int, gl_camera, GLRenderer::Free);
+cfg (Bool, gl_axes, false);
+cfg (Bool, gl_wireframe, false);
+cfg (Bool, gl_logostuds, false);
 
 // argh
 const char* g_CameraNames[7] = {
@@ -90,12 +91,16 @@
 	{ QColor (0,   160, 192), vertex (0, 0, 10000) },
 };
 
-#warning this should be a member
-static VertexCompiler g_vertexCompiler;
 
 // =============================================================================
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+#warning this should be a member
+static VertexCompiler g_vertexCompiler;
+static bool g_glInvert = false;
+static List<short> g_warnedColors;
+
 // =============================================================================
+// -----------------------------------------------------------------------------
 GLRenderer::GLRenderer (QWidget* parent) : QGLWidget (parent) {
 	m_picking = m_rangepick = false;
 	m_camera = (GL::Camera) gl_camera.value;
@@ -134,6 +139,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 GLRenderer::~GLRenderer() {
 	for (int i = 0; i < 6; ++i)
 		delete m_overlays[i].img;
@@ -143,8 +149,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::calcCameraIcons() {
 	ushort i = 0;
 	
@@ -160,6 +165,8 @@
 	}
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::initGLData() {
 	glEnable (GL_BLEND);
 	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -175,6 +182,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::resetAngles() {
 	m_rotX = 30.0f;
 	m_rotY = 325.f;
@@ -183,8 +191,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::initializeGL() {
 	setBackground();
 	
@@ -198,8 +205,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 QColor GLRenderer::getMainColor() {
 	QColor col (gl_maincolor);
 	
@@ -210,6 +216,7 @@
 	return col;
 }
 
+// =============================================================================
 // -----------------------------------------------------------------------------
 void GLRenderer::setBackground() {
 	QColor col (gl_bgcolor);
@@ -225,14 +232,14 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::refresh() {
 	update();
 	swapBuffers();
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::hardRefresh() {
 	g_vertexCompiler.compileFile();
 	refresh();
@@ -241,8 +248,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::resizeGL (int w, int h) {
 	m_width = w;
 	m_height = h;
@@ -256,6 +262,8 @@
 	glMatrixMode (GL_MODELVIEW);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::drawGLScene() {
 	if (file() == null)
 		return;
@@ -266,7 +274,7 @@
 	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 	glEnable (GL_DEPTH_TEST);
 	
-	if (m_camera != GLRenderer::Free) {
+	if (m_camera != Free) {
 		glMatrixMode (GL_PROJECTION);
 		glPushMatrix();
 		
@@ -274,9 +282,11 @@
 		glOrtho (-m_virtWidth, m_virtWidth, -m_virtHeight, m_virtHeight, -100.0f, 100.0f);
 		glTranslatef (m_panX, m_panY, 0.0f);
 		
-		glRotatef (90.0f, g_staticCameras[m_camera].glrotate[0],
-			g_staticCameras[m_camera].glrotate[1],
-			g_staticCameras[m_camera].glrotate[2]);
+		if (m_camera != Front && m_camera != Back) {
+			glRotatef (90.0f, g_staticCameras[m_camera].glrotate[0],
+				g_staticCameras[m_camera].glrotate[1],
+				g_staticCameras[m_camera].glrotate[2]);
+		}
 		
 		// Back camera needs to be handled differently
 		if (m_camera == GL::Back) {
@@ -306,7 +316,7 @@
 	} else
 		glDisable (GL_CULL_FACE);
 	
-	VertexCompiler::Array* array = g_vertexCompiler.getMergedBuffer (
+	const VertexCompiler::Array* array = g_vertexCompiler.getMergedBuffer (
 		(m_picking) ? VertexCompiler::PickArray :
 		(gl_colorbfc) ? VertexCompiler::BFCArray :
 			VertexCompiler::MainArray);
@@ -328,9 +338,10 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 // This converts a 2D point on the screen to a 3D point in the model. If 'snap'
 // is true, the 3D point will snap to the current grid.
-// =============================================================================
+// -----------------------------------------------------------------------------
 vertex GLRenderer::coordconv2_3 (const QPoint& pos2d, bool snap) const {
 	assert (camera() != Free);
 	
@@ -362,9 +373,10 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 // Inverse operation for the above - convert a 3D position to a 2D screen
 // position
-// =============================================================================
+// -----------------------------------------------------------------------------
 QPoint GLRenderer::coordconv3_2 (const vertex& pos3d) const {
 	GLfloat m[16];
 	const staticCameraMeta* cam = &g_staticCameras[m_camera];
@@ -391,8 +403,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::paintEvent (QPaintEvent* ev) {
 	Q_UNUSED (ev)
 	
@@ -429,7 +440,7 @@
 		QFontMetrics metrics = QFontMetrics (font());
 		QRect textSize = metrics.boundingRect (0, 0, m_width, m_height, Qt::AlignCenter, text);
 		
-		paint.setPen (m_darkbg ? Qt::white : Qt::black);
+		paint.setPen (getTextPen());
 		paint.drawText (m_width - textSize.width(), m_height - 16, textSize.width(),
 			textSize.height(), Qt::AlignCenter, text);
 		
@@ -517,13 +528,13 @@
 		
 		str fmtstr = tr ("%1 Camera");
 		
-		// Draw a label for the current camera in the top left corner
+		// Draw a label for the current camera in the bottom left corner
 		{
 			const ushort margin = 4;
 			
 			str label;
 			label = fmt (fmtstr, tr (g_CameraNames[camera()]));
-			paint.setPen (m_darkbg ? Qt::white : Qt::black);
+			paint.setPen (getTextPen());
 			paint.drawText (QPoint (margin, height() -  (margin + metrics.descent())), label);
 		}
 		
@@ -542,12 +553,11 @@
 	if (msglog()) {
 		int y = 0;
 		const int margin = 2;
-		QColor col = Qt::black;
-		paint.setPen (QPen());
+		QColor penColor = getTextPen();
 		
-		for (const MessageManager::Line& line : *msglog()) {
-			col.setAlphaF (line.alpha);
-			paint.setPen (QPen (col));
+		for (const MessageManager::Line& line : msglog()->getLines()) {
+			penColor.setAlphaF (line.alpha);
+			paint.setPen (penColor);
 			paint.drawText (QPoint (margin, y + margin + metrics.ascent()), line.text);
 			y += metrics.height();
 		}
@@ -571,15 +581,19 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// -----------------------------------------------------------------------------
+QColor GLRenderer::getTextPen () const {
+	return m_darkbg ? Qt::white : Qt::black;
+}
+
 // =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::compileAllObjects() {
 	g_vertexCompiler.compileFile();
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::clampAngle (double& angle) const {
 	while (angle < 0)
 		angle += 360.0;
@@ -587,6 +601,8 @@
 		angle -= 360.0;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::addDrawnVertex (vertex pos) {
 	// If we picked an already-existing vertex, stop drawing
 	for (vertex& vert : m_drawedVerts) {
@@ -600,8 +616,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::mouseReleaseEvent (QMouseEvent* ev) {
 	const bool wasLeft = (m_lastButtons & Qt::LeftButton) && !(ev->buttons() & Qt::LeftButton),
 		wasRight = (m_lastButtons & Qt::RightButton) && !(ev->buttons() & Qt::RightButton),
@@ -710,8 +725,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::mousePressEvent (QMouseEvent* ev) {
 	m_totalmove = 0;
 	
@@ -768,17 +782,19 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::keyPressEvent (QKeyEvent* ev) {
 	m_keymods = ev->modifiers();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::keyReleaseEvent (QKeyEvent* ev) {
 	m_keymods = ev->modifiers();
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::wheelEvent (QWheelEvent* ev) {
 	makeCurrent();
 	
@@ -790,6 +806,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::leaveEvent (QEvent* ev) {
 	(void) ev;
 	m_drawToolTip = false;
@@ -798,11 +815,13 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::contextMenuEvent (QContextMenuEvent* ev) {
 	g_win->spawnContextMenu (ev->globalPos());
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::setCamera (const GL::Camera cam) {
 	m_camera = cam;
 	gl_camera = (int) cam;
@@ -810,8 +829,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::pick (uint mouseX, uint mouseY) {
 	GLint viewport[4];
 	makeCurrent();
@@ -944,10 +962,13 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 READ_ACCESSOR (EditMode, GLRenderer::editMode) {
 	return m_editMode;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 SET_ACCESSOR (EditMode, GLRenderer::setEditMode) {
 	m_editMode = val;
 	
@@ -982,10 +1003,14 @@
 	update();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 READ_ACCESSOR (LDFile*, GLRenderer::file) {
 	return m_file;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 SET_ACCESSOR (LDFile*, GLRenderer::setFile) {
 	m_file = val;
 	g_vertexCompiler.setFile (val);
@@ -995,6 +1020,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::endDraw (bool accept) {
 	(void) accept;
 	
@@ -1003,7 +1029,7 @@
 	LDObject* obj = null;
 	
 	if (m_rectdraw) {
-		LDQuadObject* quad = new LDQuadObject;
+		LDQuad* quad = new LDQuad;
 		
 		// Copy the vertices from m_rectverts
 		updateRectVerts();
@@ -1017,22 +1043,22 @@
 		switch (verts.size()) {
 		case 1:
 			// 1 vertex - add a vertex object
-			obj = new LDVertexObject;
-			static_cast<LDVertexObject*> (obj)->pos = verts[0];
+			obj = new LDVertex;
+			static_cast<LDVertex*> (obj)->pos = verts[0];
 			obj->setColor (maincolor);
 			break;
 		
 		case 2:
 			// 2 verts - make a line
-			obj = new LDLineObject (verts[0], verts[1]);
+			obj = new LDLine (verts[0], verts[1]);
 			obj->setColor (edgecolor);
 			break;
 			
 		case 3:
 		case 4:
 			obj = (verts.size() == 3) ?
-				static_cast<LDObject*> (new LDTriangleObject) :
-				static_cast<LDObject*> (new LDQuadObject);
+				static_cast<LDObject*> (new LDTriangle) :
+				static_cast<LDObject*> (new LDQuad);
 			
 			obj->setColor (maincolor);
 			for (ushort i = 0; i < obj->vertices(); ++i)
@@ -1053,6 +1079,8 @@
 	m_rectdraw = false;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 static List<vertex> getVertices (LDObject* obj) {
 	List<vertex> verts;
 	
@@ -1060,7 +1088,8 @@
 		for (int i = 0; i < obj->vertices(); ++i)
 			verts << obj->getVertex (i);
 	} elif (obj->getType() == LDObject::Subfile) {
-		List<LDObject*> objs = static_cast<LDSubfileObject*> (obj)->inlineContents (true, true);
+		LDSubfile* ref = static_cast<LDSubfile*> (obj);
+		List<LDObject*> objs = ref->inlineContents (LDSubfile::DeepCacheInline);
 		
 		for(LDObject* obj : objs) {
 			verts << getVertices (obj);
@@ -1072,14 +1101,14 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::compileObject (LDObject* obj) {
 	g_vertexCompiler.compileObject (obj, obj);
 	obj->m_glinit = true;
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 uchar* GLRenderer::screencap (ushort& w, ushort& h) {
 	w = m_width;
 	h = m_height;
@@ -1096,6 +1125,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::slot_toolTipTimer() {
 	// We come here if the cursor has stayed in one place for longer than a
 	// a second. Check if we're holding it over a camera icon - if so, draw
@@ -1111,6 +1141,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::deleteLists (LDObject* obj) {
 	// Delete the lists but only if they have been initialized
 	if (!obj->m_glinit)
@@ -1123,6 +1154,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 Axis GLRenderer::cameraAxis (bool y, GL::Camera camid) {
 	if (camid == (GL::Camera) -1)
 		camid = m_camera;
@@ -1132,6 +1164,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 bool GLRenderer::setupOverlay (GL::Camera cam, str file, int x, int y, int w, int h) {
 	QImage* img = new QImage (file);
 	overlayMeta& info = getOverlay (cam);
@@ -1158,27 +1191,29 @@
 	
 	const Axis x2d = cameraAxis (false, cam),
 		y2d = cameraAxis (true, cam);
-
+	
 	double negXFac = g_staticCameras[cam].negX ? -1 : 1,
 		negYFac = g_staticCameras[cam].negY ? -1 : 1;
-
+	
 	info.v0 = info.v1 = g_origin;
 	info.v0[x2d] = - (info.ox * info.lw * negXFac) / img->width();
 	info.v0[y2d] = (info.oy * info.lh * negYFac) / img->height();
 	info.v1[x2d] = info.v0[x2d] + info.lw;
 	info.v1[y2d] = info.v0[y2d] + info.lh;
-
+	
 	// Set alpha of all pixels to 0.5
 	for (long i = 0; i < img->width(); ++i)
 	for (long j = 0; j < img->height(); ++j) {
 		uint32 pixel = img->pixel (i, j);
 		img->setPixel (i, j, 0x80000000 | (pixel & 0x00FFFFFF));
 	}
-
+	
 	updateOverlayObjects();
 	return true;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::clearOverlay() {
 	if (camera() == Free)
 		return;
@@ -1190,24 +1225,34 @@
 	updateOverlayObjects();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::setDepthValue (double depth) {
 	assert (camera() < Free);
 	m_depthValues[camera()] = depth;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 double GLRenderer::depthValue() const {
 	assert (camera() < Free);
 	return m_depthValues[camera()];
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 const char* GLRenderer::cameraName() const {
 	return g_CameraNames[camera()];
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 overlayMeta& GLRenderer::getOverlay (int newcam) {
 	 return m_overlays[newcam];
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::zoomNotch (bool inward) {
 	if (zoom() > 15)
 		setZoom (zoom() * (inward ? 0.833f : 1.2f));
@@ -1216,6 +1261,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::zoomToFit() {
 	if (file() == null) {
 		setZoom (30.0f);
@@ -1294,6 +1340,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::updateRectVerts() {
 	if (!m_rectdraw)
 		return;
@@ -1325,6 +1372,8 @@
 	m_rectverts[3][ay] = v1[ay];
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::mouseDoubleClickEvent (QMouseEvent* ev) {
 	if (!(ev->buttons() & Qt::LeftButton) || editMode() != Select)
 		return;
@@ -1341,12 +1390,14 @@
 	ev->accept();
 }
 
-LDOverlayObject* GLRenderer::findOverlayObject (GLRenderer::Camera cam) {
-	LDOverlayObject* ovlobj = null;
+// =============================================================================
+// -----------------------------------------------------------------------------
+LDOverlay* GLRenderer::findOverlayObject (GLRenderer::Camera cam) {
+	LDOverlay* ovlobj = null;
 	
-	for (LDObject * obj : *file()) {
-		if (obj->getType() == LDObject::Overlay && static_cast<LDOverlayObject*> (obj)->camera() == cam) {
-			ovlobj = static_cast<LDOverlayObject*> (obj);
+	for (LDObject * obj : file()->objects()) {
+		if (obj->getType() == LDObject::Overlay && static_cast<LDOverlay*> (obj)->camera() == cam) {
+			ovlobj = static_cast<LDOverlay*> (obj);
 			break;
 		}
 	}
@@ -1357,14 +1408,14 @@
 // =============================================================================
 // -----------------------------------------------------------------------------
 // Read in overlays from the current file and update overlay info accordingly.
-// =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::overlaysFromObjects() {
 	for (Camera cam : g_Cameras) {
 		if (cam == Free)
 			continue;
 		
 		overlayMeta& meta = m_overlays[cam];
-		LDOverlayObject* ovlobj = findOverlayObject (cam);
+		LDOverlay* ovlobj = findOverlayObject (cam);
 		
 		if (!ovlobj && meta.img) {
 			delete meta.img;
@@ -1375,13 +1426,14 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void GLRenderer::updateOverlayObjects() {
 	for (Camera cam : g_Cameras) {
 		if (cam == Free)
 			continue;
 		
 		overlayMeta& meta = m_overlays[cam];
-		LDOverlayObject* ovlobj = findOverlayObject (cam);
+		LDOverlay* ovlobj = findOverlayObject (cam);
 		
 		if (!meta.img && ovlobj) {
 			// If this is the last overlay image, we need to remove the empty space after it as well.
@@ -1399,7 +1451,7 @@
 		} elif (meta.img && !ovlobj) {
 			// Inverse case: image is there but the overlay object is
 			// not, thus create the object.
-			ovlobj = new LDOverlayObject;
+			ovlobj = new LDOverlay;
 			
 			// Find a suitable position to place this object. We want to place
 			// this into the header, which is everything up to the first scemantic
@@ -1428,7 +1480,7 @@
 				file()->insertObj (i, ovlobj);
 				
 				if (found)
-					file()->insertObj (i + 1, new LDEmptyObject);
+					file()->insertObj (i + 1, new LDEmpty);
 			}
 		}
 		
--- a/src/gldraw.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/gldraw.h	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -25,7 +25,7 @@
 
 class MessageManager;
 class QDialogButtonBox;
-class RadioBox;
+class RadioGroup;
 class QDoubleSpinBox;
 class QSpinBox;
 class QLineEdit;
@@ -147,9 +147,10 @@
 	void           clampAngle (double& angle) const;                       // Clamps an angle to [0, 360]
 	vertex         coordconv2_3 (const QPoint& pos2d, bool snap) const;    // Convert a 2D point to a 3D point
 	QPoint         coordconv3_2 (const vertex& pos3d) const;               // Convert a 3D point to a 2D point
-	LDOverlayObject* findOverlayObject (Camera cam);
+	LDOverlay*     findOverlayObject (Camera cam);
 	void           updateRectVerts();
 	void           pick (uint mouseX, uint mouseY);                        // Perform object selection
+	QColor         getTextPen() const;                                     // Determine which color to draw text with
 	
 private slots:
 	void           slot_toolTipTimer();
--- a/src/gui.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/gui.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -51,17 +51,17 @@
 
 static bool g_bSelectionLocked = false;
 
-cfg (bool, lv_colorize, true);
-cfg (str, gui_colortoolbar, "16:24:|:1:2:4:14:0:15:|:33:34:36:46");
-cfg (bool, gui_implicitfiles, false);
-extern_cfg (str, io_recentfiles);
-extern_cfg (bool, gl_axes);
-extern_cfg (str, gl_maincolor);
-extern_cfg (float, gl_maincolor_alpha);
-extern_cfg (bool, gl_wireframe);
-extern_cfg (bool, gl_colorbfc);
+cfg (Bool, lv_colorize, true);
+cfg (String, gui_colortoolbar, "16:24:|:1:2:4:14:0:15:|:33:34:36:46");
+cfg (Bool, gui_implicitfiles, false);
+extern_cfg (List, io_recentfiles);
+extern_cfg (Bool, gl_axes);
+extern_cfg (String, gl_maincolor);
+extern_cfg (Float, gl_maincolor_alpha);
+extern_cfg (Bool, gl_wireframe);
+extern_cfg (Bool, gl_colorbfc);
 
-#define act(N) extern_cfg (keyseq, key_##N);
+#define act(N) extern_cfg (KeySequence, key_##N);
 #include "actions.h"
 
 const char* g_modeActionNames[] = {
@@ -70,8 +70,7 @@
 };
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 ForgeWindow::ForgeWindow() {
 	g_win = this;
 	m_renderer = new GLRenderer;
@@ -125,12 +124,16 @@
 #include "actions.h"
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::slot_action() {
 	// Find out which action triggered this
 #define act(N) if (sender() == ui->action##N) invokeAction (ui->action##N, &actiondef_##N);
 #include "actions.h"
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::invokeAction (QAction* act, void (*func)()) {
 	beginAction (act);
 	(*func)();
@@ -138,47 +141,46 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::slot_lastSecondCleanup() {
 	delete m_renderer;
 	delete ui;
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::updateRecentFilesMenu() {
-	QStringList files = io_recentfiles.value.split ("@", QString::SkipEmptyParts);
-	QStringListIterator it (files);
-	
 	// First, clear any items in the recent files menu
 	for (QAction* recent : m_recentFiles)
 		delete recent;
 	m_recentFiles.clear();
 	
-	it.toBack();
-	while (it.hasPrevious()) {
-		str file = it.previous();
+	QAction* first = null;
+	
+	for (const QVariant& it : io_recentfiles) {
+		str file = it.toString();
 		QAction* recent = new QAction (getIcon ("open-recent"), file, this);
 		
 		connect (recent, SIGNAL (triggered()), this, SLOT (slot_recentFile()));
-		ui->menuOpenRecent->addAction (recent);
+		ui->menuOpenRecent->insertAction (first, recent);
 		m_recentFiles << recent;
+		first = recent;
 	}
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 List<LDQuickColor> quickColorsFromConfig() {
 	List<LDQuickColor> colors;
 	
 	for (str colorname : gui_colortoolbar.value.split (":")) {
-		if (colorname == "|") {
-			colors << LDQuickColor ({null, null, true});
-		} else {
+		if (colorname == "|")
+			colors << LDQuickColor::getSeparator();
+		else {
 			LDColor* col = getColor (colorname.toLong());
-			assert (col != null);
-			colors << LDQuickColor ({col, null, false});
+			
+			if (col != null)
+				colors << LDQuickColor (col, null);
 		}
 	}
 	
@@ -186,26 +188,25 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::updateToolBars() {
 	m_colorButtons.clear();
 	ui->colorToolbar->clear();
 	
 	for (LDQuickColor& entry : m_quickColors) {
-		if (entry.isSeparator)
+		if (entry.isSeparator())
 			ui->colorToolbar->addSeparator();
 		else {
 			QToolButton* colorButton = new QToolButton;
-			colorButton->setIcon (makeColorIcon (entry.col, 22));
+			colorButton->setIcon (makeColorIcon (entry.color(), 22));
 			colorButton->setIconSize (QSize (22, 22));
-			colorButton->setToolTip (entry.col->name);
+			colorButton->setToolTip (entry.color()->name);
 			
 			connect (colorButton, SIGNAL (clicked()), this, SLOT (slot_quickColor()));
 			ui->colorToolbar->addWidget (colorButton);
 			m_colorButtons << colorButton;
 			
-			entry.btn = colorButton;
+			entry.setToolButton (colorButton);
 		}
 	}
 	
@@ -213,8 +214,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::updateGridToolBar() {
 	// Ensure that the current grid - and only the current grid - is selected.
 	ui->actionGridCoarse->setChecked (grid == Grid::Coarse);
@@ -223,8 +223,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::updateTitle() {
 	str title = fmt (APPNAME " %1", fullVersionString());
 	
@@ -239,7 +238,7 @@
 			LDFile::current()->obj (0)->getType() == LDObject::Comment)
 		{
 			// Append title
-			LDCommentObject* comm = static_cast<LDCommentObject*> (LDFile::current()->obj (0));
+			LDComment* comm = static_cast<LDComment*> (LDFile::current()->obj (0));
 			title += fmt (": %1", comm->text);
 		}
 		
@@ -251,10 +250,8 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
-int ForgeWindow::deleteSelection()
-{
+// -----------------------------------------------------------------------------
+int ForgeWindow::deleteSelection() {
 	if (m_sel.size() == 0)
 		return 0;
 	
@@ -273,8 +270,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::buildObjList() {
 	if (!LDFile::current())
 		return;
@@ -289,12 +285,12 @@
 	
 	ui->objectList->clear();
 	
-	for (LDObject* obj : LDFile::current()->objs()) {
+	for (LDObject* obj : LDFile::current()->objects()) {
 		str descr;
 		
 		switch (obj->getType()) {
 		case LDObject::Comment:
-			descr = static_cast<LDCommentObject*> (obj)->text;
+			descr = static_cast<LDComment*> (obj)->text;
 			
 			// Remove leading whitespace
 			while (descr[0] == ' ')
@@ -307,7 +303,7 @@
 		case LDObject::Line:
 		case LDObject::Triangle:
 		case LDObject::Quad:
-		case LDObject::CondLine:
+		case LDObject::CndLine:
 			for (short i = 0; i < obj->vertices(); ++i) {
 				if (i != 0)
 					descr += ", ";
@@ -321,12 +317,12 @@
 			break;
 		
 		case LDObject::Vertex:
-			descr = static_cast<LDVertexObject*> (obj)->pos.stringRep (true);
+			descr = static_cast<LDVertex*> (obj)->pos.stringRep (true);
 			break;
 		
 		case LDObject::Subfile:
 			{
-				LDSubfileObject* ref = static_cast<LDSubfileObject*> (obj);
+				LDSubfile* ref = static_cast<LDSubfile*> (obj);
 				
 				descr = fmt ("%1 %2, (", ref->fileInfo()->name(),
 					ref->position().stringRep (true));
@@ -340,12 +336,12 @@
 			break;
 		
 		case LDObject::BFC:
-			descr = LDBFCObject::statements[static_cast<LDBFCObject*> (obj)->type];
-		break;
+			descr = LDBFC::statements[static_cast<LDBFC*> (obj)->type];
+			break;
 		
 		case LDObject::Overlay:
 			{
-				LDOverlayObject* ovl = static_cast<LDOverlayObject*> (obj);
+				LDOverlay* ovl = static_cast<LDOverlay*> (obj);
 				descr = fmt ("[%1] %2 (%3, %4), %5 x %6", g_CameraNames[ovl->camera()],
 					basename (ovl->filename()), ovl->x(), ovl->y(), ovl->width(), ovl->height());
 			}
@@ -357,9 +353,8 @@
 		}
 		
 		// Put it into brackets if it's hidden
-		if (obj->hidden()) {
+		if (obj->hidden())
 			descr = fmt ("[[ %1 ]]", descr);
-		}
 		
 		QListWidgetItem* item = new QListWidgetItem (descr);
 		item->setIcon (getIcon (obj->typeName()));
@@ -388,8 +383,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::scrollToSelection() {
 	if (m_sel.size() == 0)
 		return;
@@ -399,8 +393,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::slot_selectionChanged() {
 	if (g_bSelectionLocked == true || LDFile::current() == null)
 		return;
@@ -417,7 +410,7 @@
 	m_sel.clear();
 	const QList<QListWidgetItem*> items = ui->objectList->selectedItems();
 	
-	for (LDObject* obj : LDFile::current()->objs())
+	for (LDObject* obj : LDFile::current()->objects())
 	for (QListWidgetItem* item : items) {
 		if (item == obj->qObjListEntry) {
 			m_sel << obj;
@@ -440,22 +433,22 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::slot_recentFile() {
 	QAction* qAct = static_cast<QAction*> (sender());
 	openMainFile (qAct->text());
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::slot_quickColor() {
 	beginAction (null);
 	QToolButton* button = static_cast<QToolButton*> (sender());
 	LDColor* col = null;
 	
-	for (LDQuickColor entry : m_quickColors) {
-		if (entry.btn == button) {
-			col = entry.col;
+	for (const LDQuickColor& entry : m_quickColors) {
+		if (entry.toolButton() == button) {
+			col = entry.color();
 			break;
 		}
 	}
@@ -478,8 +471,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 ulong ForgeWindow::getInsertionPoint() {
 	if (m_sel.size() > 0) {
 		// If we have a selection, put the item after it.
@@ -491,25 +483,25 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::fullRefresh() {
 	buildObjList();
 	m_renderer->hardRefresh();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::refresh() {
 	buildObjList();
 	m_renderer->update();
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::updateSelection() {
 	g_bSelectionLocked = true;
 	
-	for (LDObject* obj : LDFile::current()->objs())
+	for (LDObject* obj : LDFile::current()->objects())
 		obj->setSelected (false);
 	
 	ui->objectList->clearSelection();
@@ -526,8 +518,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 bool ForgeWindow::isSelected (LDObject* obj) {
 	LDObject* needle = obj->topLevelParent();
 	
@@ -556,8 +547,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 LDObject::Type ForgeWindow::uniformSelectedType() {
 	LDObject::Type result = LDObject::Unidentified;
 	
@@ -573,8 +563,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::closeEvent (QCloseEvent* ev) {
 	// Check whether it's safe to close all files.
 	if (!safeToCloseAll()) {
@@ -584,14 +573,13 @@
 	
 	// Save the configuration before leaving so that, for instance, grid choice
 	// is preserved across instances.
-	config::save();
+	Config::save();
 	
 	ev->accept();
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::spawnContextMenu (const QPoint pos) {
 	const bool single = (g_win->sel().size() == 1);
 	LDObject* singleObj = (single) ? g_win->sel()[0] : null;
@@ -628,6 +616,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::deleteObjVector (List<LDObject*> objs) {
 	for (LDObject* obj : objs) {
 		LDFile::current()->forgetObject (obj);
@@ -636,9 +625,10 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::deleteByColor (const short colnum) {
 	List<LDObject*> objs;
-	for (LDObject* obj : LDFile::current()->objs()) {
+	for (LDObject* obj : LDFile::current()->objects()) {
 		if (!obj->isColored() || obj->color() != colnum)
 			continue;
 		
@@ -649,15 +639,18 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::updateEditModeActions() {
 	const EditMode mode = R()->editMode();
 	ACTION (ModeSelect)->setChecked (mode == Select);
 	ACTION (ModeDraw)->setChecked (mode == Draw);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::slot_editObject (QListWidgetItem* listitem) {
 	LDObject* obj = null;
-	for (LDObject* it : *LDFile::current()) {
+	for (LDObject* it : LDFile::current()->objects()) {
 		if (it->qObjListEntry == listitem) {
 			obj = it;
 			break;
@@ -667,6 +660,8 @@
 	AddObjectDialog::staticDialog (obj->getType(), obj);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::primitiveLoaderStart (ulong max) {
 	m_primLoaderWidget->show();
 	m_primLoaderBar->setRange (0, max);
@@ -674,10 +669,14 @@
 	m_primLoaderBar->setFormat ("%p%");
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::primitiveLoaderUpdate (ulong prog) {
 	m_primLoaderBar->setValue (prog);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::primitiveLoaderEnd() {
 	QTimer* hidetimer = new QTimer;
 	connect (hidetimer, SIGNAL (timeout()), m_primLoaderWidget, SLOT (hide()));
@@ -688,8 +687,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void ForgeWindow::save (LDFile* f, bool saveAs) {
 	str path = f->name();
 	
@@ -742,8 +740,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 QPixmap getIcon (str iconName) {
 	return (QPixmap (fmt (":/icons/%1.png", iconName)));
 }
@@ -788,7 +785,7 @@
 void makeColorSelector (QComboBox* box) {
 	std::map<short, ulong> counts;
 	
-	for (LDObject* obj : LDFile::current()->objs()) {
+	for (LDObject* obj : LDFile::current()->objects()) {
 		if (!obj->isColored())
 			continue;
 		
@@ -832,12 +829,12 @@
 	ui->fileList->clear();
 	
 	for (LDFile* f : g_loadedFiles) {
-		/* Don't list implicit files unless explicitly desired. */
+		// Don't list implicit files unless explicitly desired.
 		if (f->implicit() && !gui_implicitfiles)
 			continue;
 		
-		/* Add an item to the list for this file and store a pointer to it in
-		 * the file, so we can find files by the list item. */
+		// Add an item to the list for this file and store a pointer to it in
+		// the file, so we can find files by the list item.
 		ui->fileList->addItem ("");
 		QListWidgetItem* item = ui->fileList->item (ui->fileList->count() - 1);
 		f->setListItem (item);
@@ -848,25 +845,25 @@
 
 void ForgeWindow::updateFileListItem (LDFile* f) {
 	if (f->listItem() == null) {
-		/* We don't have a list item for this file, so the list either doesn't
-		 * exist yet or is out of date. Build the list now. */
+		// We don't have a list item for this file, so the list either doesn't
+		// exist yet or is out of date. Build the list now.
 		updateFileList();
 		return;
 	}
 	
-	/* If this is the current file, it also needs to be the selected item on
-	 * the list. */
+	// If this is the current file, it also needs to be the selected item on
+	// the list.
 	if (f == LDFile::current())
 		ui->fileList->setCurrentItem (f->listItem());
 	
-	/* If we list implicit files, draw them with a shade of gray to make them
-	 * distinct. */
+	// If we list implicit files, draw them with a shade of gray to make them
+	// distinct.
 	if (f->implicit())
 		f->listItem()->setForeground (QColor (96, 96, 96));
 	
 	f->listItem()->setText (f->getShortName());
 	
-	/* If the file has unsaved changes, draw a little icon next to it to mark that. */
+	// If the file has unsaved changes, draw a little icon next to it to mark that.
 	f->listItem()->setIcon (f->hasUnsavedChanges() ? getIcon ("file-save") : QIcon());
 }
 
@@ -880,20 +877,19 @@
 	// Close the history now.
 	LDFile::current()->closeHistory();
 	
-	/* Update the list item of the current file - we may need to draw an icon
-	 * now that marks it as having unsaved changes. */
+	// Update the list item of the current file - we may need to draw an icon
+	// now that marks it as having unsaved changes.
 	updateFileListItem (LDFile::current());
 }
 
 // =============================================================================
-/* A file is selected from the list of files on the left of the screen. Find out
- * which file was picked and change to it.
- */
+// A file is selected from the list of files on the left of the screen. Find out
+// which file was picked and change to it.
 void ForgeWindow::changeCurrentFile() {
 	LDFile* f = null;
 	QListWidgetItem* item = ui->fileList->currentItem();
 	
-	/* Find the file pointer of the item that was selected. */
+	// Find the file pointer of the item that was selected.
 	for (LDFile* it : g_loadedFiles) {
 		if (it->listItem() == item) {
 			f = it;
@@ -901,8 +897,8 @@
 		}
 	}
 	
-	/* If we picked the same file we're currently on, we don't need to do
-	 * anything. */
+	// If we picked the same file we're currently on, we don't need to do
+	// anything.
 	if (!f || f == LDFile::current())
 		return;
 	
@@ -926,4 +922,16 @@
 	return QImage (data, w, h, QImage::Format_ARGB32).rgbSwapped().mirrored();
 }
 
-#include "build/moc_gui.cpp"
\ No newline at end of file
+// =============================================================================
+// -----------------------------------------------------------------------------
+LDQuickColor::LDQuickColor (LDColor* color, QToolButton* toolButton) :
+	m_color (color),
+	m_toolButton (toolButton) {}
+
+LDQuickColor LDQuickColor::getSeparator() {
+	return LDQuickColor (null, null);
+}
+
+bool LDQuickColor::isSeparator() const {
+	return color() == null;
+}
\ No newline at end of file
--- a/src/gui.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/gui.h	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -46,7 +46,7 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 #define DEFINE_ACTION(NAME, DEFSHORTCUT) \
-	cfg (keyseq, key_##NAME, DEFSHORTCUT); \
+	cfg (KeySequence, key_##NAME, DEFSHORTCUT); \
 	void actiondef_##NAME()
 
 #define ACTION(N) g_win->action##N()
@@ -58,10 +58,15 @@
 #define CTRL_SHIFT(N) (Qt::CTRL | Qt::SHIFT | Qt::Key_##N)
 
 // =============================================================================
-struct LDQuickColor {
-	LDColor* col;
-	QToolButton* btn;
-	bool isSeparator;
+class LDQuickColor {
+	PROPERTY (LDColor*, color, setColor)
+	PROPERTY (QToolButton*, toolButton, setToolButton)
+	
+public:
+	LDQuickColor (LDColor* color, QToolButton* toolButton);
+	bool isSeparator() const;
+	
+	static LDQuickColor getSeparator();
 };
 
 // =============================================================================
--- a/src/gui_actions.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/gui_actions.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -29,7 +29,6 @@
 #include "history.h"
 #include "configDialog.h"
 #include "addObjectDialog.h"
-#include "aboutDialog.h"
 #include "misc.h"
 #include "gldraw.h"
 #include "dialogs.h"
@@ -37,56 +36,82 @@
 #include "ui_newpart.h"
 #include "widgets.h"
 
-extern_cfg (bool, gl_wireframe);
-extern_cfg (bool, gl_colorbfc);
+extern_cfg (Bool, gl_wireframe);
+extern_cfg (Bool, gl_colorbfc);
+extern_cfg (String, ld_defaultname);
+extern_cfg (String, ld_defaultuser);
+extern_cfg (Int, ld_defaultlicense);
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (New, CTRL_SHIFT (N)) {
 	QDialog* dlg = new QDialog (g_win);
 	Ui::NewPartUI ui;
 	ui.setupUi (dlg);
 	
+	str authortext = ld_defaultname;
+	
+	if (!ld_defaultuser.value.isEmpty())
+		authortext.append (fmt (" [%1]", ld_defaultuser));
+	
+	ui.le_author->setText (authortext);
+	
+	switch (ld_defaultlicense) {
+	case 0:
+		ui.rb_license_ca->setChecked (true);
+		break;
+	
+	case 1:
+		ui.rb_license_nonca->setChecked (true);
+		break;
+	
+	case 2:
+		ui.rb_license_none->setChecked (true);
+		break;
+	
+	default:
+		QMessageBox::warning (null, "Warning",
+			fmt ("Unknown ld_defaultlicense value %1!", ld_defaultlicense));
+		break;
+	}
+	
 	if (dlg->exec() == false)
 		return;
 	
 	newFile();
 	
-	const LDBFCObject::Type BFCType =
-		ui.rb_bfc_ccw->isChecked() ? LDBFCObject::CertifyCCW :
-		ui.rb_bfc_cw->isChecked()  ? LDBFCObject::CertifyCW : LDBFCObject::NoCertify;
+	const LDBFC::Type BFCType =
+		ui.rb_bfc_ccw->isChecked() ? LDBFC::CertifyCCW :
+		ui.rb_bfc_cw->isChecked()  ? LDBFC::CertifyCW : LDBFC::NoCertify;
 	
 	const str license =
 		ui.rb_license_ca->isChecked()    ? CALicense :
 		ui.rb_license_nonca->isChecked() ? NonCALicense : "";
 	
-	LDFile* f = LDFile::current();
-	*f << new LDCommentObject (ui.le_title->text());
-	*f << new LDCommentObject ("Name: <untitled>.dat" );
-	*f << new LDCommentObject (fmt ("Author: %1", ui.le_author->text()));
-	*f << new LDCommentObject (fmt ("!LDRAW_ORG Unofficial_Part"));
-	
-	if (license != "")
-		*f << new LDCommentObject (license);
-	
-	*f << new LDEmptyObject;
-	*f << new LDBFCObject (BFCType);
-	*f << new LDEmptyObject;
+	LDFile::current()->addObjects ({
+		new LDComment (ui.le_title->text()),
+		new LDComment ("Name: <untitled>.dat" ),
+		new LDComment (fmt ("Author: %1", ui.le_author->text())),
+		new LDComment (fmt ("!LDRAW_ORG Unofficial_Part")),
+		(license != "" ?
+			new LDComment (license) :
+			null),
+		new LDEmpty,
+		new LDBFC (BFCType),
+		new LDEmpty,
+	});
 	
 	g_win->fullRefresh();
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (NewFile, CTRL (N)) {
 	newFile();
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Open, CTRL (O)) {
 	str name = QFileDialog::getOpenFileName (g_win, "Open File", "", "LDraw files (*.dat *.ldr)");
 	
@@ -97,22 +122,19 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Save, CTRL (S)) {
 	g_win->save (LDFile::current(), false);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (SaveAs, CTRL_SHIFT (S)) {
 	g_win->save (LDFile::current(), true);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (SaveAll, CTRL (L)) {
 	for (LDFile* file : g_loadedFiles) {
 		if (file->implicit())
@@ -123,8 +145,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Close, CTRL (W)) {
 	if (!LDFile::current()->safeToClose())
 		return;
@@ -133,8 +154,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (CloseAll, 0) {
 	if (!safeToCloseAll())
 		return;
@@ -143,63 +163,79 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Settings, 0) {
-	ConfigDialog::staticDialog();
-}
-
-DEFINE_ACTION (SetLDrawPath, 0) {
-	LDrawPathDialog dlg (true);
-	dlg.exec();
+	(new ConfigDialog)->exec();
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (SetLDrawPath, 0) {
+	(new LDrawPathDialog (true))->exec();
+}
+
 // =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Exit, CTRL (Q)) {
 	exit (0);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (NewSubfile, 0) {
 	AddObjectDialog::staticDialog (LDObject::Subfile, null);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (NewLine, 0) {
 	AddObjectDialog::staticDialog (LDObject::Line, null);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (NewTriangle, 0) {
 	AddObjectDialog::staticDialog (LDObject::Triangle, null);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (NewQuad, 0) {
 	AddObjectDialog::staticDialog (LDObject::Quad, null);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (NewCLine, 0) {
-	AddObjectDialog::staticDialog (LDObject::CondLine, null);
+	AddObjectDialog::staticDialog (LDObject::CndLine, null);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (NewComment, 0) {
 	AddObjectDialog::staticDialog (LDObject::Comment, null);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (NewBFC, 0) {
 	AddObjectDialog::staticDialog (LDObject::BFC, null);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (NewVertex, 0) {
 	AddObjectDialog::staticDialog (LDObject::Vertex, null);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (MakePrimitive, 0) {
 	generatePrimitive();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Edit, 0) {
 	if (g_win->sel().size() != 1)
 		return;
@@ -208,34 +244,37 @@
 	AddObjectDialog::staticDialog (obj->getType(), obj);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Help, KEY (F1)) {
 	
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (About, 0) {
 	AboutDialog().exec();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (AboutQt, 0) {
 	QMessageBox::aboutQt (g_win);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (SelectAll, CTRL (A)) {
 	g_win->sel().clear();
 	
-	for (LDObject* obj : LDFile::current()->objs())
+	for (LDObject* obj : LDFile::current()->objects())
 		g_win->sel() << obj;
 	
 	g_win->updateSelection();
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (SelectByColor, CTRL_SHIFT (A)) {
 	short colnum = g_win->getSelectedColor();
 	
@@ -243,7 +282,7 @@
 		return; // no consensus on color
 	
 	g_win->sel().clear();
-	for (LDObject* obj : LDFile::current()->objs())
+	for (LDObject* obj : LDFile::current()->objects())
 		if (obj->color() == colnum)
 			g_win->sel() << obj;
 	
@@ -251,6 +290,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (SelectByType, 0) {
 	if (g_win->sel().size() == 0)
 		return;
@@ -265,19 +305,19 @@
 	str refName;
 	
 	if (type == LDObject::Subfile) {
-		refName = static_cast<LDSubfileObject*> (g_win->sel()[0])->fileInfo()->name();
+		refName = static_cast<LDSubfile*> (g_win->sel()[0])->fileInfo()->name();
 		
 		for (LDObject* obj : g_win->sel())
-			if (static_cast<LDSubfileObject*> (obj)->fileInfo()->name() != refName)
+			if (static_cast<LDSubfile*> (obj)->fileInfo()->name() != refName)
 				return;
 	}
 	
 	g_win->sel().clear();
-	for (LDObject* obj : LDFile::current()->objs()) {
+	for (LDObject* obj : LDFile::current()->objects()) {
 		if (obj->getType() != type)
 			continue;
 		
-		if (type == LDObject::Subfile && static_cast<LDSubfileObject*> (obj)->fileInfo()->name() != refName)
+		if (type == LDObject::Subfile && static_cast<LDSubfile*> (obj)->fileInfo()->name() != refName)
 			continue;
 		
 		g_win->sel() << obj;
@@ -287,8 +327,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (GridCoarse, 0) {
 	grid = Grid::Coarse;
 	g_win->updateGridToolBar();
@@ -305,16 +344,14 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (ResetView, CTRL (0)) {
 	g_win->R()->resetAngles();
 	g_win->R()->update();
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (InsertFrom, 0) {
 	str fname = QFileDialog::getOpenFileName();
 	ulong idx = g_win->getInsertionPoint();
@@ -345,8 +382,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (ExportTo, 0) {
 	if (g_win->sel().size() == 0)
 		return;
@@ -370,8 +406,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (InsertRaw, 0) {
 	ulong idx = g_win->getInsertionPoint();
 	
@@ -405,6 +440,8 @@
 	g_win->scrollToSelection();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Screenshot, 0) {
 	setlocale (LC_ALL, "C");
 	
@@ -427,7 +464,8 @@
 }
 
 // =============================================================================
-extern_cfg (bool, gl_axes);
+// -----------------------------------------------------------------------------
+extern_cfg (Bool, gl_axes);
 DEFINE_ACTION (Axes, 0) {
 	gl_axes = !gl_axes;
 	ACTION (Axes)->setChecked (gl_axes);
@@ -435,6 +473,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Visibility, 0) {
 	for (LDObject* obj : g_win->sel())
 		obj->setHidden (!obj->hidden());
@@ -442,13 +481,16 @@
 	g_win->refresh();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Wireframe, 0) {
 	gl_wireframe = !gl_wireframe;
 	g_win->R()->refresh();
 }
 
-DEFINE_ACTION (SetOverlay,  0)
-{
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (SetOverlay,  0) {
 	OverlayDialog dlg;
 	
 	if (!dlg.exec())
@@ -458,18 +500,26 @@
 		dlg.ofsy(), dlg.lwidth(), dlg.lheight() );
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (ClearOverlay, 0) {
 	g_win->R()->clearOverlay();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (ModeSelect, CTRL (1)) {
 	g_win->R()->setEditMode (Select);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (ModeDraw, CTRL (2)) {
 	g_win->R()->setEditMode (Draw);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (SetDrawDepth, 0) {
 	if (g_win->R()->camera() == GL::Free)
 		return;
@@ -527,12 +577,37 @@
 }
 #endif
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (ScanPrimitives, 0) {
 	PrimitiveLister::start();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (BFCView, SHIFT (B)) {
 	gl_colorbfc = !gl_colorbfc;
 	ACTION (BFCView)->setChecked (gl_colorbfc);
 	g_win->R()->refresh();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (JumpTo, CTRL (G)) {
+	bool ok;
+	int defval = 0;
+	LDObject* obj;
+	
+	if (g_win->sel().size() == 1)
+		defval = g_win->sel()[0]->getIndex();
+	
+	int idx = QInputDialog::getInt (null, "Go to line", "Go to line:", defval,
+		1, LDFile::current()->numObjs(), 1, &ok);
+	
+	if (!ok || (obj = LDFile::current()->object (idx - 1)) == null)
+		return;
+	
+	g_win->clearSelection();
+	g_win->sel() << obj;
+	g_win->updateSelection();
 }
\ No newline at end of file
--- a/src/gui_editactions.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/gui_editactions.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -33,12 +33,13 @@
 #include "ui_replcoords.h"
 #include "ui_editraw.h"
 #include "ui_flip.h"
+#include "ui_addhistoryline.h"
 
-cfg (bool, edit_schemanticinline, false);
+cfg (Bool, edit_schemanticinline, false);
+extern_cfg (String, ld_defaultuser);
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 static int copyToClipboard() {
 	List<LDObject*> objs = g_win->sel();
 	int num = 0;
@@ -62,8 +63,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Cut, CTRL (X)) {
 	int num = copyToClipboard();
 	g_win->deleteSelection();
@@ -71,16 +71,14 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Copy, CTRL (C)) {
 	int num = copyToClipboard();
 	log (ForgeWindow::tr ("%1 objects copied"), num);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Paste, CTRL (V)) {
 	const str clipboardText = qApp->clipboard()->text();
 	ulong idx = g_win->getInsertionPoint();
@@ -101,16 +99,14 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Delete, KEY (Delete)) {
 	int num = g_win->deleteSelection();
 	log (ForgeWindow::tr ("%1 objects deleted"), num);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 static void doInline (bool deep) {
 	List<LDObject*> sel = g_win->sel();
 	
@@ -125,7 +121,11 @@
 		List<LDObject*> objs;
 		
 		if (obj->getType() == LDObject::Subfile)
-			objs = static_cast<LDSubfileObject*> (obj)->inlineContents (deep, true);
+			objs = static_cast<LDSubfile*> (obj)->inlineContents (
+				(LDSubfile::InlineFlags)
+				((deep) ? LDSubfile::DeepInline : 0) |
+				LDSubfile::CacheInline
+			);
 		else
 			continue;
 		
@@ -156,9 +156,8 @@
 	doInline (true);
 }
 
-// ===============================================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// ===============================================================================================
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (SplitQuads, 0) {
 	List<LDObject*> objs = g_win->sel();
 	int num = 0;
@@ -173,14 +172,14 @@
 		if (index == -1)
 			return;
 		
-		List<LDTriangleObject*> triangles = static_cast<LDQuadObject*> (obj)->splitToTriangles();
+		List<LDTriangle*> triangles = static_cast<LDQuad*> (obj)->splitToTriangles();
 		
 		// Replace the quad with the first triangle and add the second triangle
 		// after the first one.
 		LDFile::current()->setObject (index, triangles[0]);
 		LDFile::current()->insertObj (index + 1, triangles[1]);
 		
-		for (LDTriangleObject * t : triangles)
+		for (LDTriangle * t : triangles)
 			g_win->R()->compileObject (t);
 		
 		// Delete this quad now, it has been split.
@@ -194,8 +193,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (EditRaw, KEY (F9)) {
 	if (g_win->sel().size() != 1)
 		return;
@@ -208,7 +206,7 @@
 	ui.code->setText (obj->raw());
 	
 	if (obj->getType() == LDObject::Error)
-		ui.errorDescription->setText (static_cast<LDErrorObject*> (obj)->reason);
+		ui.errorDescription->setText (static_cast<LDError*> (obj)->reason);
 	else {
 		ui.errorDescription->hide();
 		ui.errorIcon->hide();
@@ -229,8 +227,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (SetColor, KEY (C)) {
 	if (g_win->sel().size() <= 0)
 		return;
@@ -259,8 +256,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Borders, CTRL_SHIFT (B)) {
 	List<LDObject*> objs = g_win->sel();
 	int num = 0;
@@ -270,23 +266,23 @@
 			continue;
 		
 		short numLines;
-		LDLineObject* lines[4];
+		LDLine* lines[4];
 		
 		if (obj->getType() == LDObject::Quad) {
 			numLines = 4;
 			
-			LDQuadObject* quad = static_cast<LDQuadObject*> (obj);
-			lines[0] = new LDLineObject (quad->getVertex (0), quad->getVertex (1));
-			lines[1] = new LDLineObject (quad->getVertex (1), quad->getVertex (2));
-			lines[2] = new LDLineObject (quad->getVertex (2), quad->getVertex (3));
-			lines[3] = new LDLineObject (quad->getVertex (3), quad->getVertex (0));
+			LDQuad* quad = static_cast<LDQuad*> (obj);
+			lines[0] = new LDLine (quad->getVertex (0), quad->getVertex (1));
+			lines[1] = new LDLine (quad->getVertex (1), quad->getVertex (2));
+			lines[2] = new LDLine (quad->getVertex (2), quad->getVertex (3));
+			lines[3] = new LDLine (quad->getVertex (3), quad->getVertex (0));
 		} else {
 			numLines = 3;
 			
-			LDTriangleObject* tri = static_cast<LDTriangleObject*> (obj);
-			lines[0] = new LDLineObject (tri->getVertex (0), tri->getVertex (1));
-			lines[1] = new LDLineObject (tri->getVertex (1), tri->getVertex (2));
-			lines[2] = new LDLineObject (tri->getVertex (2), tri->getVertex (0));
+			LDTriangle* tri = static_cast<LDTriangle*> (obj);
+			lines[0] = new LDLine (tri->getVertex (0), tri->getVertex (1));
+			lines[1] = new LDLine (tri->getVertex (1), tri->getVertex (2));
+			lines[2] = new LDLine (tri->getVertex (2), tri->getVertex (0));
 		}
 		
 		for (short i = 0; i < numLines; ++i) {
@@ -305,8 +301,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (CornerVerts, 0) {
 	int num = 0;
 	
@@ -317,7 +312,7 @@
 		ulong idx = obj->getIndex();
 		
 		for (short i = 0; i < obj->vertices(); ++i) {
-			LDVertexObject* vert = new LDVertexObject;
+			LDVertex* vert = new LDVertex;
 			vert->pos = obj->getVertex (i);
 			vert->setColor (obj->color());
 			
@@ -332,14 +327,15 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 static void doMoveSelection (const bool up) {
 	List<LDObject*> objs = g_win->sel();
 	LDObject::moveObjects (objs, up);
 	g_win->buildObjList();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (MoveUp, KEY (PageUp)) {
 	doMoveSelection (true);
 }
@@ -349,8 +345,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Undo, CTRL (Z)) {
 	LDFile::current()->undo();
 }
@@ -360,8 +355,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void doMoveObjects (vertex vect) {
 	// Apply the grid values
 	vect[X] *= currentGrid().confs[Grid::X]->value;
@@ -376,8 +370,10 @@
 	g_win->refresh();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (MoveXNeg, KEY (Left)) {
-	doMoveObjects ({ -1, 0, 0});
+	doMoveObjects ({-1, 0, 0});
 }
 
 DEFINE_ACTION (MoveYNeg, KEY (Home)) {
@@ -401,6 +397,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Invert, CTRL_SHIFT (W)) {
 	List<LDObject*> sel = g_win->sel();
 	
@@ -413,34 +410,36 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 static void rotateVertex (vertex& v, const vertex& rotpoint, const matrix& transform) {
 	v.move (-rotpoint);
 	v.transform (transform, g_origin);
 	v.move (rotpoint);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 static void doRotate (const short l, const short m, const short n) {
 	List<LDObject*> sel = g_win->sel();
 	List<vertex*> queue;
 	const vertex rotpoint = rotPoint (sel);
-	const double angle = (pi * currentGrid().confs[Grid::Angle]->value) / 180;
-	
-	// ref: http://en.wikipedia.org/wiki/Transformation_matrix#Rotation_2
-	const double cosangle = cos (angle),
+	const double angle = (pi * currentGrid().confs[Grid::Angle]->value) / 180,
+		cosangle = cos (angle),
 		sinangle = sin (angle);
 	
+	// ref: http://en.wikipedia.org/wiki/Transformation_matrix#Rotation_2
 	matrix transform ({
-		(l* l * (1 - cosangle)) + cosangle,
-		(m* l * (1 - cosangle)) - (n* sinangle),
-		(n* l * (1 - cosangle)) + (m* sinangle),
+		(l * l * (1 - cosangle)) + cosangle,
+		(m * l * (1 - cosangle)) - (n * sinangle),
+		(n * l * (1 - cosangle)) + (m * sinangle),
 		
-		(l* m * (1 - cosangle)) + (n* sinangle),
-		(m* m * (1 - cosangle)) + cosangle,
-		(n* m * (1 - cosangle)) - (l* sinangle),
+		(l * m * (1 - cosangle)) + (n * sinangle),
+		(m * m * (1 - cosangle)) + cosangle,
+		(n * m * (1 - cosangle)) - (l * sinangle),
 		
-		(l* n * (1 - cosangle)) - (m* sinangle),
-		(m* n * (1 - cosangle)) + (l* sinangle),
-		(n* n * (1 - cosangle)) + cosangle
+		(l * n * (1 - cosangle)) - (m * sinangle),
+		(m * n * (1 - cosangle)) + (l * sinangle),
+		(n * n * (1 - cosangle)) + cosangle
 	});
 	
 	// Apply the above matrix to everything
@@ -453,12 +452,16 @@
 			}
 		} elif (obj->hasMatrix()) {
 			LDMatrixObject* mo = dynamic_cast<LDMatrixObject*> (obj);
+			
+			// Transform the position
 			vertex v = mo->position();
 			rotateVertex (v, rotpoint, transform);
 			mo->setPosition (v);
+			
+			// Transform the matrix
 			mo->setTransform (mo->transform() * transform);
 		} elif (obj->getType() == LDObject::Vertex) {
-			LDVertexObject* vert = static_cast<LDVertexObject*> (obj);
+			LDVertex* vert = static_cast<LDVertex*> (obj);
 			vertex v = vert->pos;
 			rotateVertex (v, rotpoint, transform);
 			vert->pos = v;
@@ -470,6 +473,8 @@
 	g_win->refresh();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (RotateXPos, CTRL (Right)) { doRotate (1, 0, 0); }
 DEFINE_ACTION (RotateYPos, CTRL (End))   { doRotate (0, 1, 0); }
 DEFINE_ACTION (RotateZPos, CTRL (Up))    { doRotate (0, 0, 1); }
@@ -482,8 +487,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (RoundCoordinates, 0) {
 	setlocale (LC_ALL, "C");
 	int num = 0;
@@ -509,8 +513,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Uncolorize, 0) {
 	int num = 0;
 	
@@ -520,7 +523,7 @@
 		
 		int col = maincolor;
 		
-		if (obj->getType() == LDObject::Line || obj->getType() == LDObject::CondLine)
+		if (obj->getType() == LDObject::Line || obj->getType() == LDObject::CndLine)
 			col = edgecolor;
 		
 		obj->setColor (col);
@@ -533,6 +536,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (ReplaceCoords, CTRL (R)) {
 	QDialog* dlg = new QDialog (g_win);
 	Ui::ReplaceCoordsUI ui;
@@ -577,7 +581,8 @@
 	g_win->refresh();
 }
 
-// ================================================================================================
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Flip, CTRL_SHIFT (F)) {
 	QDialog* dlg = new QDialog;
 	Ui::FlipUI ui;
@@ -605,16 +610,17 @@
 	g_win->refresh();
 }
 
-// ================================================================================================
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Demote, 0) {
 	List<LDObject*> sel = g_win->sel();
 	int num = 0;
 	
 	for (LDObject* obj : sel) {
-		if (obj->getType() != LDObject::CondLine)
+		if (obj->getType() != LDObject::CndLine)
 			continue;
 		
-		LDLineObject* repl = static_cast<LDCondLineObject*> (obj)->demote();
+		LDLine* repl = static_cast<LDCndLine*> (obj)->demote();
 		g_win->R()->compileObject (repl);
 		++num;
 	}
@@ -623,15 +629,18 @@
 	g_win->refresh();
 }
 
-// =================================================================================================
+// =============================================================================
+// -----------------------------------------------------------------------------
 static bool isColorUsed (short colnum) {
-	for (LDObject* obj : LDFile::current()->objs())
+	for (LDObject* obj : LDFile::current()->objects())
 		if (obj->isColored() && obj->color() == colnum)
 			return true;
 	
 	return false;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 DEFINE_ACTION (Autocolor, 0) {
 	short colnum = 0;
 	
@@ -639,8 +648,7 @@
 		colnum++;
 	
 	if (colnum >= MAX_COLORS) {
-		//: Auto-colorer error message
-		critical (ForgeWindow::tr ("Out of unused colors! What are you doing?!"));
+		log (ForgeWindow::tr ("Cannot auto-color: all colors are in use!"));
 		return;
 	}
 	
@@ -654,4 +662,60 @@
 	
 	log (ForgeWindow::tr ("Auto-colored: new color is [%1] %2"), colnum, getColor (colnum)->name);
 	g_win->refresh();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+DEFINE_ACTION (AddHistoryLine, 0) {
+	LDObject* obj;
+	bool ishistory = false,
+		prevIsHistory = false;
+	
+	QDialog* dlg = new QDialog;
+	Ui_AddHistoryLine* ui = new Ui_AddHistoryLine;
+	ui->setupUi (dlg);
+	ui->m_username->setText (ld_defaultuser);
+	ui->m_date->setDate (QDate::currentDate());
+	ui->m_comment->setFocus();
+	
+	if (!dlg->exec())
+		return;
+	
+	// Create the comment object based on input
+	str commentText = fmt ("!HISTORY %1 [%2] %3",
+		ui->m_date->date().toString("yyyy-MM-dd"),
+		ui->m_username->text(),
+		ui->m_comment->text());
+	
+	LDComment* comm = new LDComment (commentText);
+	
+	// Find a spot to place the new comment
+	for (
+		obj = LDFile::current()->object (0);
+		obj && obj->next() && !obj->next()->isScemantic();
+		obj = obj->next()
+	) {
+		LDComment* comm = dynamic_cast<LDComment*> (obj);
+		if (comm && comm->text.startsWith ("!HISTORY "))
+			ishistory = true;
+		
+		if (prevIsHistory && !ishistory) {
+			// Last line was history, this isn't, thus insert the new history
+			// line here.
+			break;
+		}
+		
+		prevIsHistory = ishistory;
+	}
+	
+	int idx = obj ? obj->getIndex() : 0;
+	LDFile::current()->insertObj (idx++, comm);
+	
+	// If we're adding a history line right before a scemantic object, pad it
+	// an empty line
+	if (obj && obj->next() && obj->next()->isScemantic())
+		LDFile::current()->insertObj (idx, new LDEmpty);
+	
+	g_win->buildObjList();
+	delete ui;
 }
\ No newline at end of file
--- a/src/history.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/history.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -25,10 +25,14 @@
 
 bool g_fullRefresh = false;
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 History::History() :
 	m_pos (-1),
 	m_opened (false) {}
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void History::undo() {
 	if (m_changesets.size() == 0 || pos() == -1)
 		return;
@@ -50,6 +54,8 @@
 	updateActions();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void History::redo() {
 	if (pos() == (long) m_changesets.size())
 		return;
@@ -71,6 +77,8 @@
 	updateActions();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void History::clear() {
 	for (List<AbstractHistoryEntry*> set : m_changesets)
 		for (AbstractHistoryEntry * change : set)
@@ -79,11 +87,15 @@
 	m_changesets.clear();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void History::updateActions() const {
 	ACTION (Undo)->setEnabled (pos() != -1);
 	ACTION (Redo)->setEnabled (pos() < (long) m_changesets.size() - 1);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void History::open() {
 	if (opened())
 		return;
@@ -91,6 +103,8 @@
 	setOpened (true);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void History::close() {
 	if (!opened())
 		return;
@@ -109,6 +123,8 @@
 	updateActions();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void History::add (AbstractHistoryEntry* entry) {
 	if (!opened()) {
 		delete entry;
@@ -120,6 +136,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void AddHistory::undo() const {
 	LDFile* f = parent()->file();
 	LDObject* obj = f->object (index());
@@ -129,6 +146,8 @@
 	g_fullRefresh = true;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void AddHistory::redo() const {
 	LDFile* f = parent()->file();
 	LDObject* obj = parseLine (code());
@@ -140,6 +159,7 @@
 
 // =============================================================================
 // heh
+// -----------------------------------------------------------------------------
 void DelHistory::undo() const {
 	LDFile* f = parent()->file();
 	LDObject* obj = parseLine (code());
@@ -147,6 +167,8 @@
 	g_win->R()->compileObject (obj);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void DelHistory::redo() const {
 	LDFile* f = parent()->file();
 	LDObject* obj = f->object (index());
@@ -159,6 +181,7 @@
 DelHistory::~DelHistory() {}
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void EditHistory::undo() const {
 	LDObject* obj = LDFile::current()->object (index());
 	LDObject* newobj = parseLine (oldCode());
@@ -166,6 +189,8 @@
 	g_win->R()->compileObject (newobj);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void EditHistory::redo() const {
 	LDObject* obj = LDFile::current()->object (index());
 	LDObject* newobj = parseLine (newCode());
@@ -176,6 +201,7 @@
 EditHistory::~EditHistory() {}
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void SwapHistory::undo() const {
 	LDObject::fromID (a)->swap (LDObject::fromID (b));
 }
--- a/src/history.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/history.h	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
--- a/src/labeledwidget.h	Fri Aug 16 11:05:21 2013 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- *  LDForge: LDraw parts authoring CAD
- *  Copyright (C) 2013 Santeri Piippo
- *  
- *  This program is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation, either version 3 of the License, or
- *  (at your option) any later version.
- *  
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *  
- *  You should have received a copy of the GNU General Public License
- *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef LABELEDWIDGET_H
-#define LABELEDWIDGET_H
-
-#include "common.h"
-#include <QLabel>
-#include <QBoxLayout>
-
-// =============================================================================
-// LabeledWidget
-//
-// Convenience class for a widget with a label beside it.
-// =============================================================================
-template<class R> class LabeledWidget : public QWidget {
-	PROPERTY (QLabel*, label, setLabel)
-	PROPERTY (R*, widget, setWidget)
-	
-public:
-	explicit LabeledWidget (const char* labelstr, QWidget* parent = null) : QWidget (parent) {
-		m_widget = new R (this);
-		commonInit (labelstr);
-	}
-	
-	explicit LabeledWidget (const char* labelstr, R* widget, QWidget* parent = null) :
-		QWidget (parent), m_widget (widget) {
-		
-		commonInit (labelstr);
-	}
-	
-	explicit LabeledWidget (QWidget* parent = 0, Qt::WindowFlags f = 0) {
-		m_widget = new R (this);
-		commonInit ("");
-	}
-	
-	R* w() const { return m_widget; }
-	operator R*() { return m_widget; }
-	
-private:
-	Q_DISABLE_COPY (LabeledWidget<R>)
-	
-	void commonInit (const char* labelstr) {
-		m_label = new QLabel (labelstr, this);
-		m_layout = new QHBoxLayout;
-		m_layout->addWidget (m_label);
-		m_layout->addWidget (m_widget);
-		setLayout (m_layout);
-	}
-	
-	QHBoxLayout* m_layout;
-};
-
-#endif // LABELEDWIDGET_H
\ No newline at end of file
--- a/src/ldconfig.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/ldconfig.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -24,6 +24,7 @@
 
 // =============================================================================
 // Helper function for parseLDConfig
+// -----------------------------------------------------------------------------
 static bool parseLDConfigTag (LDConfigParser& pars, char const* tag, str& val) {
 	short pos;
 	
@@ -36,13 +37,13 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void parseLDConfig() {
 	File* f = openLDrawFile ("LDConfig.ldr", false);
 	
 	if (!f) {
 		critical (fmt (QObject::tr ("Unable to open LDConfig.ldr for parsing! (%1)"),
 			strerror (errno)));
-		delete f;
 		return;
 	}
 	
@@ -110,23 +111,25 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 LDConfigParser::LDConfigParser (str inText, char sep) {
 	m_tokens = container_cast<QStringList, List<str>> (inText.split (sep, QString::SkipEmptyParts));
 	m_pos = -1;
 }
 
+// =============================================================================
 // -----------------------------------------------------------------------------
 bool LDConfigParser::atBeginning() {
 	return (m_pos == -1);
 }
 
+// =============================================================================
 // -----------------------------------------------------------------------------
 bool LDConfigParser::atEnd() {
 	return (m_pos == (signed) m_tokens.size() - 1);
 }
 
+// =============================================================================
 // -----------------------------------------------------------------------------
 bool LDConfigParser::getToken (str& val, const ushort pos) {
 	if (pos >= m_tokens.size())
@@ -136,16 +139,19 @@
 	return true;
 }
 
+// =============================================================================
 // -----------------------------------------------------------------------------
 bool LDConfigParser::next (str& val) {
 	return getToken (val, ++m_pos);
 }
 
+// =============================================================================
 // -----------------------------------------------------------------------------
 bool LDConfigParser::peekNext (str& val) {
 	return getToken (val, m_pos + 1);
 }
 
+// =============================================================================
 // -----------------------------------------------------------------------------
 bool LDConfigParser::findToken (short& result, char const* needle, short args) {
 	for (ushort i = 0; i < (m_tokens.size() - args); ++i) {
@@ -158,21 +164,25 @@
 	return false;
 }
 
+// =============================================================================
 // -----------------------------------------------------------------------------
 void LDConfigParser::rewind() {
 	m_pos = -1;
 }
 
+// =============================================================================
 // -----------------------------------------------------------------------------
 void LDConfigParser::seek (short amount, bool rel) {
 	m_pos = (rel ? m_pos : 0) + amount;
 }
 
+// =============================================================================
 // -----------------------------------------------------------------------------
 size_t LDConfigParser::size() {
 	return m_tokens.size();
 }
 
+// =============================================================================
 // -----------------------------------------------------------------------------
 bool LDConfigParser::tokenCompare (short inPos, const char* sOther) {
 	str tok;
--- a/src/ldtypes.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/ldtypes.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -25,21 +25,24 @@
 #include "gldraw.h"
 #include "colors.h"
 
+cfg (String, ld_defaultname, "");
+cfg (String, ld_defaultuser, "");
+cfg (Int, ld_defaultlicense, 0);
+
 // List of all LDObjects
 List<LDObject*> g_LDObjects;
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
 // LDObject constructors
+// -----------------------------------------------------------------------------
 LDObject::LDObject() :
 	m_hidden (false),
 	m_selected (false),
 	m_parent (null),
 	m_file (null),
 	qObjListEntry (null),
-	m_glinit (false) {
-	
+	m_glinit (false)
+{
 	// Determine ID
 	int32 id = 1; // 0 is invalid
 	
@@ -51,9 +54,11 @@
 	g_LDObjects << this;
 }
 
+// =============================================================================
 // Default implementations for LDObject's virtual methods. These should never be
 // actually called, for a subclass-less LDObject should never come into existance.
 // These exist only to satisfy the linker.
+// -----------------------------------------------------------------------------
 LDObject::Type LDObject::getType() const {
 	return LDObject::Unidentified;
 }
@@ -79,20 +84,24 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void LDObject::setVertexCoord (int i, Axis ax, double value) {
 	vertex v = getVertex (i);
 	v[ax] = value;
 	setVertex (i, v);
 }
 
-LDErrorObject::LDErrorObject() {}
+LDError::LDError() {}
 
 // =============================================================================
-str LDCommentObject::raw() {
+// -----------------------------------------------------------------------------
+str LDComment::raw() {
 	return fmt ("0 %1", text);
 }
 
-str LDSubfileObject::raw() {
+// =============================================================================
+// -----------------------------------------------------------------------------
+str LDSubfile::raw() {
 	str val = fmt ("1 %1 %2 ", color(), position());
 	val += transform().stringRep();
 	val += ' ';
@@ -100,7 +109,9 @@
 	return val;
 }
 
-str LDLineObject::raw() {
+// =============================================================================
+// -----------------------------------------------------------------------------
+str LDLine::raw() {
 	str val = fmt ("2 %1", color());
 	
 	for (ushort i = 0; i < 2; ++i)
@@ -109,7 +120,9 @@
 	return val;
 }
 
-str LDTriangleObject::raw() {
+// =============================================================================
+// -----------------------------------------------------------------------------
+str LDTriangle::raw() {
 	str val = fmt ("3 %1", color());
 	
 	for (ushort i = 0; i < 3; ++i)
@@ -118,7 +131,9 @@
 	return val;
 }
 
-str LDQuadObject::raw() {
+// =============================================================================
+// -----------------------------------------------------------------------------
+str LDQuad::raw() {
 	str val = fmt ("4 %1", color());
 	
 	for (ushort i = 0; i < 4; ++i)
@@ -127,7 +142,9 @@
 	return val;
 }
 
-str LDCondLineObject::raw() {
+// =============================================================================
+// -----------------------------------------------------------------------------
+str LDCndLine::raw() {
 	str val = fmt ("5 %1", color());
 	
 	// Add the coordinates
@@ -137,22 +154,27 @@
 	return val;
 }
 
-str LDErrorObject::raw() {
+// =============================================================================
+// -----------------------------------------------------------------------------
+str LDError::raw() {
 	return contents;
 }
 
-str LDVertexObject::raw() {
+// =============================================================================
+// -----------------------------------------------------------------------------
+str LDVertex::raw() {
 	return fmt ("0 !LDFORGE VERTEX %1 %2", color(), pos);
 }
 
-str LDEmptyObject::raw() {
+// =============================================================================
+// -----------------------------------------------------------------------------
+str LDEmpty::raw() {
 	return "";
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
-const char* LDBFCObject::statements[] = {
+// -----------------------------------------------------------------------------
+const char* LDBFC::statements[] = {
 	"CERTIFY CCW",
 	"CCW",
 	"CERTIFY CW",
@@ -165,66 +187,70 @@
 	"NOCLIP",
 };
 
-str LDBFCObject::raw() {
-	return fmt ("0 BFC %1", LDBFCObject::statements[type]);
+str LDBFC::raw() {
+	return fmt ("0 BFC %1", LDBFC::statements[type]);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
-List<LDTriangleObject*> LDQuadObject::splitToTriangles() {
+// -----------------------------------------------------------------------------
+List<LDTriangle*> LDQuad::splitToTriangles() {
 	// Create the two triangles based on this quadrilateral:
 	// 0---3       0---3    3
 	// |   |       |  /    /|
 	// |   |  ==>  | /    / |
 	// |   |       |/    /  |
 	// 1---2       1    1---2
-	LDTriangleObject* tri1 = new LDTriangleObject (getVertex (0), getVertex (1), getVertex (3));
-	LDTriangleObject* tri2 = new LDTriangleObject (getVertex (1), getVertex (2), getVertex (3));
+	LDTriangle* tri1 = new LDTriangle (getVertex (0), getVertex (1), getVertex (3));
+	LDTriangle* tri2 = new LDTriangle (getVertex (1), getVertex (2), getVertex (3));
 	
 	// The triangles also inherit the quad's color
 	tri1->setColor (color());
 	tri2->setColor (color());
 	
-	List<LDTriangleObject*> triangles;
+	List<LDTriangle*> triangles;
 	triangles << tri1;
 	triangles << tri2;
 	return triangles;
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void LDObject::replace (LDObject* other) {
 	long idx = getIndex();
 	assert (idx != -1);
 	
 	// Replace the instance of the old object with the new object
-	LDFile::current()->setObject (idx, other);
+	file()->setObject (idx, other);
 	
 	// Remove the old object
 	delete this;
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void LDObject::swap (LDObject* other) {
-	for (LDObject*& obj : *LDFile::current()) {
+	int i = 0;
+	for (LDObject* obj : file()->objects()) {
 		if (obj == this)
-			obj = other;
+			file()->setObject (i, other);
 		elif (obj == other)
-			obj = this;
+			file()->setObject (i, this);
+		
+		++i;
 	}
 
-	LDFile::current()->addToHistory (new SwapHistory (id(), other->id()));
+	file()->addToHistory (new SwapHistory (id(), other->id()));
 }
 
-LDLineObject::LDLineObject (vertex v1, vertex v2) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+LDLine::LDLine (vertex v1, vertex v2) {
 	setVertex (0, v1);
 	setVertex (1, v2);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 LDObject::~LDObject() {
 	// Remove this object from the selection array if it is there.
 	for (ulong i = 0; i < g_win->sel().size(); ++i)
@@ -242,12 +268,11 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 static void transformObject (LDObject* obj, matrix transform, vertex pos, short parentcolor) {
 	switch (obj->getType()) {
 	case LDObject::Line:
-	case LDObject::CondLine:
+	case LDObject::CndLine:
 	case LDObject::Triangle:
 	case LDObject::Quad:
 		for (short i = 0; i < obj->vertices(); ++i) {
@@ -260,7 +285,7 @@
 
 	case LDObject::Subfile:
 		{
-			LDSubfileObject* ref = static_cast<LDSubfileObject*> (obj);
+			LDSubfile* ref = static_cast<LDSubfile*> (obj);
 			matrix newMatrix = transform * ref->transform();
 			vertex newpos = ref->position();
 			
@@ -279,50 +304,9 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
-List<LDObject*> LDSubfileObject::inlineContents (bool deep, bool cache) {
-	List<LDObject*> objs, objcache;
-
-	// If we have this cached, just clone that
-	if (deep && fileInfo()->cache().size()) {
-		for (LDObject* obj : fileInfo()->cache())
-			objs << obj->clone();
-	} else {
-		if (!deep)
-			cache = false;
-		
-		for (LDObject* obj : *fileInfo()) {
-			// Skip those without scemantic meaning
-			if (!obj->isScemantic())
-				continue;
-			
-			// Got another sub-file reference, inline it if we're deep-inlining. If not,
-			// just add it into the objects normally. Also, we only cache immediate
-			// subfiles and this is not one. Yay, recursion!
-			if (deep && obj->getType() == LDObject::Subfile) {
-				LDSubfileObject* ref = static_cast<LDSubfileObject*> (obj);
-				
-				List<LDObject*> otherobjs = ref->inlineContents (true, false);
-				
-				for (LDObject* otherobj : otherobjs) {
-					// Cache this object, if desired
-					if (cache)
-						objcache << otherobj->clone();
-					
-					objs << otherobj;
-				}
-			} else {
-				if (cache)
-					objcache << obj->clone();
-				
-				objs << obj->clone();
-			}
-		}
-		
-		if (cache)
-			fileInfo()->setCache (objcache);
-	}
+// -----------------------------------------------------------------------------
+List<LDObject*> LDSubfile::inlineContents (InlineFlags flags) {
+	List<LDObject*> objs = fileInfo()->inlineContents (flags);
 	
 	// Transform the objects
 	for (LDObject* obj : objs) {
@@ -335,8 +319,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 long LDObject::getIndex() const {
 #ifndef RELEASE
 	assert (file() != null);
@@ -350,14 +333,17 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void LDObject::moveObjects (List<LDObject*> objs, const bool up) {
+	if (objs.size() == 0)
+		return;
+	
 	// If we move down, we need to iterate the array in reverse order.
 	const long start = up ? 0 : (objs.size() - 1);
 	const long end = up ? objs.size() : -1;
 	const long incr = up ? 1 : -1;
 	List<LDObject*> objsToCompile;
+	LDFile* file = objs[0]->file();
 	
 	for (long i = start; i != end; i += incr) {
 		LDObject* obj = objs[i];
@@ -365,7 +351,7 @@
 		const long idx = obj->getIndex(),
 			target = idx + (up ? -1 : 1);
 		
-		if ((up && idx == 0) || (!up && idx == (long) (LDFile::current()->objs().size() - 1))) {
+		if ((up && idx == 0) || (!up && idx == (long) (file->objects().size() - 1))) {
 			// One of the objects hit the extrema. If this happens, this should be the first
 			// object to be iterated on. Thus, nothing has changed yet and it's safe to just
 			// abort the entire operation.
@@ -374,9 +360,9 @@
 		}
 		
 		objsToCompile << obj;
-		objsToCompile << LDFile::current()->obj (target);
+		objsToCompile << file->obj (target);
 		
-		obj->swap (LDFile::current()->obj (target));
+		obj->swap (file->obj (target));
 	}
 	
 	objsToCompile.makeUnique();
@@ -387,6 +373,8 @@
 		g_win->R()->compileObject (obj);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 str LDObject::typeName (LDObject::Type type) {
 	LDObject* obj = LDObject::getDefault (type);
 	str name = obj->typeName();
@@ -395,8 +383,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 str LDObject::objectListContents (const List<LDObject*>& objs) {
 	bool firstDetails = true;
 	str text = "";
@@ -432,6 +419,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 LDObject* LDObject::topLevelParent() {
 	if (!parent())
 		return this;
@@ -445,17 +433,19 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 LDObject* LDObject::next() const {
 	long idx = getIndex();
 	assert (idx != -1);
 	
-	if (idx == (long) LDFile::current()->numObjs() - 1)
+	if (idx == (long) file()->numObjs() - 1)
 		return null;
 	
-	return LDFile::current()->obj (idx + 1);
+	return file()->obj (idx + 1);
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 LDObject* LDObject::prev() const {
 	long idx = getIndex();
 	assert (idx != -1);
@@ -463,58 +453,68 @@
 	if (idx == 0)
 		return null;
 	
-	return LDFile::current()->obj (idx - 1);
+	return file()->obj (idx - 1);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void LDObject::move (vertex vect)    { (void) vect; }
-void LDEmptyObject::move (vertex vect)     { (void) vect; }
-void LDBFCObject::move (vertex vect)       { (void) vect; }
-void LDCommentObject::move (vertex vect)   { (void) vect; }
-void LDErrorObject::move (vertex vect) { (void) vect; }
+void LDEmpty::move (vertex vect)     { (void) vect; }
+void LDBFC::move (vertex vect)       { (void) vect; }
+void LDComment::move (vertex vect)   { (void) vect; }
+void LDError::move (vertex vect) { (void) vect; }
 
-void LDVertexObject::move (vertex vect) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+void LDVertex::move (vertex vect) {
 	pos += vect;
 }
 
-void LDSubfileObject::move (vertex vect) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+void LDSubfile::move (vertex vect) {
 	setPosition (position() + vect);
 }
 
-void LDLineObject::move (vertex vect) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+void LDLine::move (vertex vect) {
 	for (short i = 0; i < 2; ++i)
 		setVertex (i, getVertex (i) + vect);
 }
 
-void LDTriangleObject::move (vertex vect) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+void LDTriangle::move (vertex vect) {
 	for (short i = 0; i < 3; ++i)
 		setVertex (i, getVertex (i) + vect);
 }
 
-void LDQuadObject::move (vertex vect) {
-	for (short i = 0; i < 4; ++i)
-		setVertex (i, getVertex (i) + vect);
-}
-
-void LDCondLineObject::move (vertex vect) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+void LDQuad::move (vertex vect) {
 	for (short i = 0; i < 4; ++i)
 		setVertex (i, getVertex (i) + vect);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// -----------------------------------------------------------------------------
+void LDCndLine::move (vertex vect) {
+	for (short i = 0; i < 4; ++i)
+		setVertex (i, getVertex (i) + vect);
+}
+
 // =============================================================================
+// -----------------------------------------------------------------------------
 #define CHECK_FOR_OBJ(N) \
 	if (type == LDObject::N) \
-		return new LD##N##Object;
+		return new LD##N;
 
 LDObject* LDObject::getDefault (const LDObject::Type type) {
 	CHECK_FOR_OBJ (Comment)
 	CHECK_FOR_OBJ (BFC)
 	CHECK_FOR_OBJ (Line)
-	CHECK_FOR_OBJ (CondLine)
+	CHECK_FOR_OBJ (CndLine)
 	CHECK_FOR_OBJ (Subfile)
 	CHECK_FOR_OBJ (Triangle)
 	CHECK_FOR_OBJ (Quad)
@@ -527,15 +527,16 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void LDObject::invert() {}
-void LDBFCObject::invert() {}
-void LDEmptyObject::invert() {}
-void LDCommentObject::invert() {}
-void LDErrorObject::invert() {}
+void LDBFC::invert() {}
+void LDEmpty::invert() {}
+void LDComment::invert() {}
+void LDError::invert() {}
 
-void LDTriangleObject::invert() {
+// =============================================================================
+// -----------------------------------------------------------------------------
+void LDTriangle::invert() {
 	// Triangle goes 0 -> 1 -> 2, reversed: 0 -> 2 -> 1.
 	// Thus, we swap 1 and 2.
 	vertex tmp = getVertex (1);
@@ -545,7 +546,9 @@
 	return;
 }
 
-void LDQuadObject::invert() {
+// =============================================================================
+// -----------------------------------------------------------------------------
+void LDQuad::invert() {
 	// Quad: 0 -> 1 -> 2 -> 3
 	// rev:  0 -> 3 -> 2 -> 1
 	// Thus, we swap 1 and 3.
@@ -554,7 +557,9 @@
 	setVertex (3, tmp);
 }
 
-void LDSubfileObject::invert() {
+// =============================================================================
+// -----------------------------------------------------------------------------
+void LDSubfile::invert() {
 	// Subfiles are inverted when they're prefixed with
 	// a BFC INVERTNEXT statement. Thus we need to toggle this status.
 	// For flat primitives it's sufficient that the determinant is
@@ -564,21 +569,23 @@
 	ulong idx = getIndex();
 	
 	if (idx > 0) {
-		LDBFCObject* bfc = dynamic_cast<LDBFCObject*> (prev());
+		LDBFC* bfc = dynamic_cast<LDBFC*> (prev());
 		
-		if (bfc && bfc->type == LDBFCObject::InvertNext) {
+		if (bfc && bfc->type == LDBFC::InvertNext) {
 			// This is prefixed with an invertnext, thus remove it.
-			LDFile::current()->forgetObject (bfc);
+			file()->forgetObject (bfc);
 			delete bfc;
 			return;
 		}
 	}
 	
 	// Not inverted, thus prefix it with a new invertnext.
-	LDBFCObject* bfc = new LDBFCObject (LDBFCObject::InvertNext);
-	LDFile::current()->insertObj (idx, bfc);
+	LDBFC* bfc = new LDBFC (LDBFC::InvertNext);
+	file()->insertObj (idx, bfc);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 static void invertLine (LDObject* line) {
 	// For lines, we swap the vertices. I don't think that a
 	// cond-line's control points need to be swapped, do they?
@@ -587,19 +594,20 @@
 	line->setVertex (1, tmp);
 }
 
-void LDLineObject::invert() {
+void LDLine::invert() {
 	invertLine (this);
 }
 
-void LDCondLineObject::invert() {
+void LDCndLine::invert() {
 	invertLine (this);
 }
 
-void LDVertexObject::invert() {}
+void LDVertex::invert() {}
 
 // =============================================================================
-LDLineObject* LDCondLineObject::demote() {
-	LDLineObject* repl = new LDLineObject;
+// -----------------------------------------------------------------------------
+LDLine* LDCndLine::demote() {
+	LDLine* repl = new LDLine;
 	
 	for (int i = 0; i < repl->vertices(); ++i)
 		repl->setVertex (i, getVertex (i));
@@ -610,6 +618,8 @@
 	return repl;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 LDObject* LDObject::fromID (int id) {
 	for (LDObject* obj : g_LDObjects)
 		if (obj->id() == id)
@@ -619,21 +629,23 @@
 }
 
 // =============================================================================
-str LDOverlayObject::raw() {
+// -----------------------------------------------------------------------------
+str LDOverlay::raw() {
 	return fmt ("0 !LDFORGE OVERLAY %1 %2 %3 %4 %5 %6",
 		filename(), camera(), x(), y(), width(), height());
 }
 
-void LDOverlayObject::move (vertex vect) {
+void LDOverlay::move (vertex vect) {
 	Q_UNUSED (vect)
 }
 
-void LDOverlayObject::invert() {}
+void LDOverlay::invert() {}
 
 // =============================================================================
 // Hook the set accessors of certain properties to this changeProperty function.
 // It takes care of history management so we can capture low-level changes, this
 // makes history stuff work out of the box.
+// -----------------------------------------------------------------------------
 template<class T> void changeProperty (LDObject* obj, T* ptr, const T& val) {
 	long idx;
 	
@@ -642,11 +654,13 @@
 		*ptr = val;
 		str after = obj->raw();
 		
-		LDFile::current()->addToHistory (new EditHistory (idx, before, after));
+		obj->file()->addToHistory (new EditHistory (idx, before, after));
 	} else
 		*ptr = val;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 READ_ACCESSOR (short, LDObject::color) {
 	return m_color;
 }
@@ -655,6 +669,8 @@
 	changeProperty (this, &m_color, val);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 const vertex& LDObject::getVertex (int i) const {
 	return m_coords[i];
 }
@@ -663,6 +679,8 @@
 	changeProperty (this, &m_coords[i], vert);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 READ_ACCESSOR (vertex, LDMatrixObject::position) {
 	return m_position;
 }
@@ -671,6 +689,8 @@
 	changeProperty (linkPointer(), &m_position, val);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 READ_ACCESSOR (matrix, LDMatrixObject::transform) {
 	return m_transform;
 }
--- a/src/ldtypes.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/ldtypes.h	Sat Sep 07 13:23:09 2013 +0300
@@ -24,15 +24,15 @@
 
 #define LDOBJ(T) \
 public: \
-	virtual ~LD##T##Object() {} \
+	virtual ~LD##T() {} \
 	virtual LDObject::Type getType() const override { \
 		return LDObject::T; \
 	} \
 	virtual str raw(); \
-	virtual LD##T##Object* clone() { \
-		return new LD##T##Object (*this); \
+	virtual LD##T* clone() { \
+		return new LD##T (*this); \
 	} \
-	virtual void move (vertex vVector); \
+	virtual void move (vertex where); \
 	virtual void invert();
 
 #define LDOBJ_NAME(N)          virtual str typeName() const override { return #N; }
@@ -50,7 +50,7 @@
 #define LDOBJ_NO_MATRIX        LDOBJ_SETMATRIX (false)
 
 class QListWidgetItem;
-class LDSubfileObject;
+class LDSubfile;
 class LDFile;
 
 // =============================================================================
@@ -76,7 +76,7 @@
 		Quad,           // Object represents a quadrilateral
 		Triangle,       // Object represents a triangle
 		Line,           // Object represents a line
-		CondLine,       // Object represents a conditional line
+		CndLine,        // Object represents a conditional line
 		Vertex,         // Object is a vertex, LDForge extension object
 		BFC,            // Object represents a BFC statement
 		Overlay,        // Object contains meta-info about an overlay image.
@@ -166,14 +166,14 @@
 };
 
 // =============================================================================
-// LDErrorObject
+// LDError
 //
 // Represents a line in the LDraw file that could not be properly parsed. It is
 // represented by a (!) ERROR in the code view. It exists for the purpose of
 // allowing garbage lines be debugged and corrected within LDForge. The member
 // zContent contains the contents of the unparsable line.
 // =============================================================================
-class LDErrorObject : public LDObject {
+class LDError : public LDObject {
 	LDOBJ (Error)
 	LDOBJ_NAME (error)
 	LDOBJ_VERTICES (0)
@@ -183,8 +183,8 @@
 	PROPERTY (str, fileRef, setFileRef)
 
 public:
-	LDErrorObject();
-	LDErrorObject (str contents, str reason) : contents (contents), reason (reason) {}
+	LDError();
+	LDError (str contents, str reason) : contents (contents), reason (reason) {}
 
 	// Content of this unknown line
 	str contents;
@@ -194,11 +194,11 @@
 };
 
 // =============================================================================
-// LDEmptyObject
+// LDEmpty
 //
 // Represents an empty line in the LDraw code file.
 // =============================================================================
-class LDEmptyObject : public LDObject {
+class LDEmpty : public LDObject {
 	LDOBJ (Empty)
 	LDOBJ_VERTICES (0)
 	LDOBJ_UNCOLORED
@@ -207,12 +207,12 @@
 };
 
 // =============================================================================
-// LDCommentObject
+// LDComment
 //
 // Represents a code-0 comment in the LDraw code file. Member text contains
 // the text of the comment.
 // =============================================================================
-class LDCommentObject : public LDObject {
+class LDComment : public LDObject {
 	LDOBJ (Comment)
 	LDOBJ_NAME (comment)
 	LDOBJ_VERTICES (0)
@@ -221,19 +221,19 @@
 	LDOBJ_NO_MATRIX
 
 public:
-	LDCommentObject() {}
-	LDCommentObject (str text) : text (text) {}
+	LDComment() {}
+	LDComment (str text) : text (text) {}
 
 	str text; // The text of this comment
 };
 
 // =============================================================================
-// LDBFCObject
+// LDBFC
 //
 // Represents a 0 BFC statement in the LDraw code. eStatement contains the type
 // of this statement.
 // =============================================================================
-class LDBFCObject : public LDObject {
+class LDBFC : public LDObject {
 public:
 	enum Type {
 		CertifyCCW,
@@ -257,8 +257,8 @@
 	LDOBJ_NO_MATRIX
 	
 public:
-	LDBFCObject() {}
-	LDBFCObject (const LDBFCObject::Type type) : type (type) {}
+	LDBFC() {}
+	LDBFC (const LDBFC::Type type) : type (type) {}
 
 	// Statement strings
 	static const char* statements[];
@@ -267,11 +267,11 @@
 };
 
 // =============================================================================
-// LDSubfileObject
+// LDSubfile
 //
 // Represents a single code-1 subfile reference.
 // =============================================================================
-class LDSubfileObject : public LDObject, public LDMatrixObject {
+class LDSubfile : public LDObject, public LDMatrixObject {
 	LDOBJ (Subfile)
 	LDOBJ_NAME (subfile)
 	LDOBJ_VERTICES (0)
@@ -281,23 +281,35 @@
 	PROPERTY (LDFile*, fileInfo, setFileInfo)
 
 public:
-	LDSubfileObject() {
+	enum InlineFlag {
+		DeepInline     = (1 << 0),
+		CacheInline    = (1 << 1),
+		RendererInline = (1 << 2),
+		
+		DeepCacheInline = DeepInline | CacheInline,
+	};
+	
+	Q_DECLARE_FLAGS (InlineFlags, InlineFlag)
+	
+	LDSubfile() {
 		setLinkPointer (this);
 	}
-
+	
 	// Inlines this subfile. Note that return type is an array of heap-allocated
 	// LDObject-clones, they must be deleted one way or another.
-	List<LDObject*> inlineContents (bool deep, bool cache);
+	List<LDObject*> inlineContents (InlineFlags flags);
 };
 
+Q_DECLARE_OPERATORS_FOR_FLAGS (LDSubfile::InlineFlags)
+
 // =============================================================================
-// LDLineObject
+// LDLine
 //
 // Represents a single code-2 line in the LDraw code file. v0 and v1 are the end
 // points of the line. The line is colored with dColor unless uncolored mode is
 // set.
 // =============================================================================
-class LDLineObject : public LDObject {
+class LDLine : public LDObject {
 	LDOBJ (Line)
 	LDOBJ_NAME (line)
 	LDOBJ_VERTICES (2)
@@ -306,18 +318,18 @@
 	LDOBJ_NO_MATRIX
 
 public:
-	LDLineObject() {}
-	LDLineObject (vertex v1, vertex v2);
+	LDLine() {}
+	LDLine (vertex v1, vertex v2);
 };
 
 // =============================================================================
-// LDCondLineObject
+// LDCndLine
 //
 // Represents a single code-5 conditional line. The end-points v0 and v1 are
 // inherited from LDLine, c0 and c1 are the control points of this line.
 // =============================================================================
-class LDCondLineObject : public LDLineObject {
-	LDOBJ (CondLine)
+class LDCndLine : public LDLine {
+	LDOBJ (CndLine)
 	LDOBJ_NAME (condline)
 	LDOBJ_VERTICES (4)
 	LDOBJ_COLORED
@@ -325,18 +337,18 @@
 	LDOBJ_NO_MATRIX
 
 public:
-	LDCondLineObject() {}
-	LDLineObject* demote();
+	LDCndLine() {}
+	LDLine* demote();
 };
 
 // =============================================================================
-// LDTriangleObject
+// LDTriangle
 //
 // Represents a single code-3 triangle in the LDraw code file. Vertices v0, v1
 // and v2 contain the end-points of this triangle. dColor is the color the
 // triangle is colored with.
 // =============================================================================
-class LDTriangleObject : public LDObject {
+class LDTriangle : public LDObject {
 	LDOBJ (Triangle)
 	LDOBJ_NAME (triangle)
 	LDOBJ_VERTICES (3)
@@ -345,8 +357,8 @@
 	LDOBJ_NO_MATRIX
 
 public:
-	LDTriangleObject() {}
-	LDTriangleObject (vertex v0, vertex v1, vertex v2) {
+	LDTriangle() {}
+	LDTriangle (vertex v0, vertex v1, vertex v2) {
 		setVertex (0, v0);
 		setVertex (1, v1);
 		setVertex (2, v2);
@@ -354,12 +366,12 @@
 };
 
 // =============================================================================
-// LDQuadObject
+// LDQuad
 //
 // Represents a single code-4 quadrilateral. v0, v1, v2 and v3 are the end points
 // of the quad, dColor is the color used for the quad.
 // =============================================================================
-class LDQuadObject : public LDObject {
+class LDQuad : public LDObject {
 public:
 	LDOBJ (Quad)
 	LDOBJ_NAME (quad)
@@ -368,21 +380,21 @@
 	LDOBJ_SCEMANTIC
 	LDOBJ_NO_MATRIX
 
-	LDQuadObject() {}
+	LDQuad() {}
 
 	// Split this quad into two triangles (note: heap-allocated)
-	List<LDTriangleObject*> splitToTriangles();
+	List<LDTriangle*> splitToTriangles();
 };
 
 // =============================================================================
-// LDVertexObject
+// LDVertex
 //
 // The vertex is an LDForce-specific extension which represents a single
 // vertex which can be used as a parameter to tools or to store coordinates
 // with. Vertices are a part authoring tool and they should not appear in
 // finished parts.
 // =============================================================================
-class LDVertexObject : public LDObject {
+class LDVertex : public LDObject {
 public:
 	LDOBJ (Vertex)
 	LDOBJ_NAME (vertex)
@@ -391,18 +403,18 @@
 	LDOBJ_NON_SCEMANTIC
 	LDOBJ_NO_MATRIX
 
-	LDVertexObject() {}
+	LDVertex() {}
 
 	vertex pos;
 };
 
 // =============================================================================
-// LDOverlayObject
+// LDOverlay
 //
 // Overlay image meta, stored in the header of parts so as to preserve overlay
 // information.
 // =============================================================================
-class LDOverlayObject : public LDObject {
+class LDOverlay : public LDObject {
 	LDOBJ (Overlay)
 	LDOBJ_NAME (overlay)
 	LDOBJ_VERTICES (0)
--- a/src/main.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/main.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -19,7 +19,7 @@
 #include <QApplication>
 #include <QMessageBox>
 #include <QAbstractButton>
-#include <qfile.h>
+#include <QFile>
 #include <QTextStream>
 #include "gui.h"
 #include "file.h"
@@ -28,6 +28,8 @@
 #include "colors.h"
 #include "types.h"
 #include "primitives.h"
+#include "gldraw.h"
+#include "configDialog.h"
 
 List<LDFile*> g_loadedFiles;
 ForgeWindow* g_win = null; 
@@ -39,12 +41,57 @@
 const vertex g_origin (0.0f, 0.0f, 0.0f);
 const matrix g_identity ({1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f});
 
+cfg (Bool, firststart, true);
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+int main (int argc, char* argv[]) {
+	QApplication app (argc, argv);
+	app.setOrganizationName (APPNAME);
+	app.setApplicationName (APPNAME);
+	
+	g_app = &app;
+	LDFile::setCurrent (null);
+	
+	// Load or create the configuration
+	if (!Config::load()) {
+		print ("Creating configuration file...\n");
+		if (Config::save())
+			print ("Configuration file successfully created.\n");
+		else
+			print ("failed to create configuration file!\n");
+	}
+	
+	LDPaths::initPaths();
+	initColors();
+	loadLogoedStuds();
+	
+	ForgeWindow* win = new ForgeWindow;
+	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 (firststart) {
+		(new ConfigDialog (ConfigDialog::ProfileTab))->exec();
+		firststart = false;
+		Config::save();
+	}
+	
+	loadPrimitives();
+	return app.exec();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
 void doPrint (File& f, initlist<StringFormatArg> args) {
 	str msg = DoFormat (args);
 	f.write (msg.toUtf8());
 	f.flush();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void doPrint (FILE* fp, initlist<StringFormatArg> args) {
 	if (fp == stdout)
 		doPrint (g_file_stdout, args);
@@ -55,47 +102,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
-int main (int argc, char* argv[]) {
-	QApplication app (argc, argv);
-	app.setOrganizationName (APPNAME);
-	app.setApplicationName (APPNAME);
-	
-	g_app = &app;
-	LDFile::setCurrent (null);
-	
-	// Load or create the configuration
-	if (!config::load()) {
-		print ("Creating configuration file...\n");
-		if (config::save())
-			print ("Configuration file successfully created.\n");
-		else
-			print ("failed to create configuration file!\n");
-	}
-	
-	LDPaths::initPaths();
-	initColors();
-
-	ForgeWindow* win = new ForgeWindow;
-	
-	newFile();
-	loadPrimitives();
-	
-	win->show();
-	return app.exec();
-}
-
-void doDevf (const char* func, const char* fmtstr, ...) {
-	va_list va;
-	
-	printf ("%s: ", func);
-	
-	va_start (va, fmtstr);
-	vprintf (fmtstr, va);
-	va_end (va);
-}
-
+// -----------------------------------------------------------------------------
 str versionString() {
 	if (g_versionString.length() == 0) {
 #if VERSION_PATCH == 0
@@ -108,6 +115,8 @@
 	return g_versionString;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 str versionMoniker() {
 #if BUILD_ID == BUILD_INTERNAL
 	return "Internal";
@@ -122,10 +131,14 @@
 #endif // BUILD_ID
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 str fullVersionString() {
 	return fmt ("v%1 %2", versionString(), versionMoniker());
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 static void bombBox (str msg) {
 	msg.replace ("\n", "<br />");
 	
@@ -141,6 +154,8 @@
 	box.exec();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void assertionFailure (const char* file, const ulong line, const char* funcname, const char* expr) {
 	str errmsg = fmt ("File: %1\nLine: %2:\nFunction %3:\n\nAssertion `%4' failed",
 		file, line, funcname, expr);
@@ -162,6 +177,8 @@
 #endif
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void fatalError (const char* file, const ulong line, const char* funcname, str msg) {
 	str errmsg = fmt ("Aborting over a call to fatal():\nFile: %1\nLine: %2\nFunction: %3\n\n%4",
 		file, line, funcname, msg);
--- a/src/messagelog.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/messagelog.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -1,26 +1,26 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <QTimer>
+#include <QDate>
 #include "messagelog.h"
 #include "gldraw.h"
 #include "gui.h"
-#include <QTimer>
-#include <QDate>
 
 static const unsigned int g_maxMessages = 5;
 static const int g_expiry = 5;
@@ -46,6 +46,7 @@
 // Check this line's expiry and update alpha accordingly. Returns true if the
 // line is to still stick around, false if it expired. 'changed' is updated to
 // whether the line has somehow changed since the last update.
+// -----------------------------------------------------------------------------
 bool MessageManager::Line::update (bool& changed) {
 	changed = false;
 	QDateTime now = QDateTime::currentDateTime();
@@ -67,8 +68,8 @@
 }
 
 // =============================================================================
+// Add a line to the message manager.
 // -----------------------------------------------------------------------------
-// Add a line to the message manager.
 void MessageManager::addLine (str line) {
 	// If there's too many entries, pop the excess out
 	while (m_lines.size() >= g_maxMessages)
@@ -82,17 +83,9 @@
 }
 
 // =============================================================================
-// -----------------------------------------------------------------------------
-// Shortcut
-MessageManager& MessageManager::operator<< (str line) {
-	addLine (line);
-	return *this;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
 // Ticks the message manager. All lines are ticked and the renderer scene is
 // redrawn if something changed.
+// -----------------------------------------------------------------------------
 void MessageManager::tick() {
 	if (m_lines.size() == 0)
 		return;
@@ -114,27 +107,18 @@
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-// C++11-for loop support
-MessageManager::c_it MessageManager::begin() const {
-	return m_lines.begin();
+const List<MessageManager::Line>& MessageManager::getLines() const {
+	return m_lines;
 }
 
 // =============================================================================
-// -----------------------------------------------------------------------------
-MessageManager::c_it MessageManager::end() const {
-	return m_lines.end();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
 // log() interface - format the argument list and add the resulting string to
 // the main message manager.
+// -----------------------------------------------------------------------------
 void DoLog (std::initializer_list<StringFormatArg> args) {
 	const str msg = DoFormat (args);
 	g_win->addMessage (msg);
 	
 	// Also print it to stdout
 	print ("%1\n", msg);
-}
-
-#include "build/moc_messagelog.cpp"
\ No newline at end of file
+}
\ No newline at end of file
--- a/src/messagelog.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/messagelog.h	Sat Sep 07 13:23:09 2013 +0300
@@ -53,15 +53,9 @@
 		QDateTime expiry;
 	};
 	
-	typedef List<Line>::it it;
-	typedef List<Line>::c_it c_it;
-	
 	explicit MessageManager (QObject* parent = 0);
 	void addLine (str line);
-	c_it begin() const;
-	c_it end() const;
-	
-	MessageManager& operator<< (str line);
+	const List<Line>& getLines() const;
 	
 private:
 	List<Line> m_lines;
--- a/src/misc.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/misc.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -1,24 +1,24 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <math.h>
 #include <locale.h>
-#include <qcolor.h>
+#include <QColor>
 #include "common.h"
 #include "misc.h"
 #include "gui.h"
@@ -80,27 +80,26 @@
 };
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 // Grid stuff
-cfg (int, grid, Grid::Medium);
+cfg (Int, grid, Grid::Medium);
 
-cfg (float, grid_coarse_x,			5.0f);
-cfg (float, grid_coarse_y,			5.0f);
-cfg (float, grid_coarse_z,			5.0f);
-cfg (float, grid_coarse_angle,	45.0f);
-cfg (float, grid_medium_x,			1.0f);
-cfg (float, grid_medium_y,			1.0f);
-cfg (float, grid_medium_z,			1.0f);
-cfg (float, grid_medium_angle,	22.5f);
-cfg (float, grid_fine_x,			0.1f);
-cfg (float, grid_fine_y,			0.1f);
-cfg (float, grid_fine_z,			0.1f);
-cfg (float, grid_fine_angle,		7.5f);
-cfg (int, edit_rotpoint, 0);
-cfg (float, edit_rotpoint_x, 0.0f); // TODO: make a vertexconfig object and use it here
-cfg (float, edit_rotpoint_y, 0.0f);
-cfg (float, edit_rotpoint_z, 0.0f);
+cfg (Float, grid_coarse_x,			5.0f);
+cfg (Float, grid_coarse_y,			5.0f);
+cfg (Float, grid_coarse_z,			5.0f);
+cfg (Float, grid_coarse_angle,	45.0f);
+cfg (Float, grid_medium_x,			1.0f);
+cfg (Float, grid_medium_y,			1.0f);
+cfg (Float, grid_medium_z,			1.0f);
+cfg (Float, grid_medium_angle,	22.5f);
+cfg (Float, grid_fine_x,			0.1f);
+cfg (Float, grid_fine_y,			0.1f);
+cfg (Float, grid_fine_z,			0.1f);
+cfg (Float, grid_fine_angle,		7.5f);
+cfg (Int, edit_rotpoint, 0);
+cfg (Float, edit_rotpoint_x, 0.0f); // TODO: make a VertexConfig and use it here
+cfg (Float, edit_rotpoint_y, 0.0f);
+cfg (Float, edit_rotpoint_z, 0.0f);
 
 const gridinfo g_GridInfo[3] = {
 	{ "Coarse", { &grid_coarse_x, &grid_coarse_y, &grid_coarse_z, &grid_coarse_angle }},
@@ -110,6 +109,7 @@
 
 // =============================================================================
 // Snap the given coordinate value on the current grid's given axis.
+// -----------------------------------------------------------------------------
 double Grid::snap (double in, const Grid::Config axis) {
 	const double gridval = currentGrid().confs[axis]->value;
 	const long mult = abs (in / gridval);
@@ -126,9 +126,9 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
 // Float to string. Removes trailing zeroes and is locale-independant.
+// TODO: Replace with QString::number()
+// -----------------------------------------------------------------------------
 str ftoa (double num) {
 	// Disable the locale first so that the decimal point will not
 	// turn into anything weird (like commas)
@@ -150,8 +150,8 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// TODO: I guess Qt must have something like this stashed somewhere?
+// -----------------------------------------------------------------------------
 bool isNumber (const str& tok) {
 	bool gotDot = false;
 	
@@ -180,8 +180,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void simplify (short& numer, short& denom) {
 	bool repeat;
 	
@@ -205,6 +204,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 vertex rotPoint (const List<LDObject*>& objs) {
 	LDBoundingBox box;
 	
@@ -229,6 +229,8 @@
 	return vertex();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void configRotationPoint() {
 	QDialog* dlg = new QDialog;
 	Ui::RotPointUI ui;
@@ -265,6 +267,8 @@
 	edit_rotpoint_z = ui.customZ->value();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 str join (initlist<StringFormatArg> vals, str delim) {
 	QStringList list;
 	for (const StringFormatArg& arg : vals)
@@ -273,6 +277,9 @@
 	return list.join (delim);
 }
 
+// =============================================================================
+// TODO: I'm quite sure Qt has this covered as well.
+// -----------------------------------------------------------------------------
 double atof (str val) {
 	// Disable the locale while parsing the line or atof's behavior changes
 	// between locales (i.e. fails to read decimals properly). That is
--- a/src/misc.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/misc.h	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -47,10 +47,10 @@
 // Grid stuff
 typedef struct {
 	const char* const name;
-	floatconfig* const confs[4];
+	FloatConfig* const confs[4];
 } gridinfo;
 
-extern_cfg (int, grid);
+extern_cfg (Int, grid);
 static const short g_NumGrids = 3;
 extern const gridinfo g_GridInfo[3];
 
@@ -94,7 +94,7 @@
 	};
 	
 	double snap (double value, const Grid::Config axis);
-};
+}
 
 // =============================================================================
 template<class T> void dataswap (T& a, T& b) {
--- a/src/primitives.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/primitives.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -32,20 +32,28 @@
 static bool g_primListerMutex = false;
 List<Primitive> g_primitives;
 
-static const str g_Other = QObject::tr ("Other");
+static const str g_Other = PrimitiveLister::tr ("Other");
+
+static const str g_radialNameRoots[] = {
+	"edge",
+	"cyli",
+	"disc",
+	"ndis",
+	"ring",
+	"con"
+};
 
 static void populateCategories();
 static void loadPrimitiveCatgories();
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void loadPrimitives() {
 	print ("Loading primitives...\n");
 	loadPrimitiveCatgories();
 	
 	// Try to load prims.cfg
-	File conf (config::filepath ("prims.cfg"), File::Read);
+	File conf (Config::filepath ("prims.cfg"), File::Read);
 	
 	if (!conf) {
 		// No prims.cfg, build it
@@ -69,8 +77,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 static void recursiveGetFilenames (QDir dir, List<str>& fnames) {
 	QFileInfoList flist = dir.entryInfoList();
 	
@@ -86,8 +93,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void PrimitiveLister::work() {
 	g_activePrimLister = this;
 	m_prims.clear();
@@ -118,13 +124,13 @@
 			info.title.remove (0, 1);  // remove 0
 			info.title = info.title.simplified();
 		}
-
+		
 		m_prims << info;
 		emit update (++i);
 	}
 	
 	// Save to a config file
-	File conf (config::filepath ("prims.cfg"), File::Write);
+	File conf (Config::filepath ("prims.cfg"), File::Write);
 	
 	for (Primitive & info : m_prims)
 		fprint (conf, "%1 %2\n", info.name, info.title);
@@ -140,8 +146,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void PrimitiveLister::start() {
 	if (g_activePrimLister)
 		return;
@@ -157,6 +162,8 @@
 	listerThread->start();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 static PrimitiveCategory* findCategory (str name) {
 	for (PrimitiveCategory& cat : g_PrimitiveCategories)
 		if (cat.name() == name)
@@ -166,8 +173,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 static void populateCategories() {
 	for (PrimitiveCategory& cat : g_PrimitiveCategories)
 		cat.prims.clear();
@@ -180,7 +186,7 @@
 		cat.setName (g_Other);
 		unmatched = & (g_PrimitiveCategories << cat);
 	}
-
+	
 	for (Primitive& prim : g_primitives) {
 		bool matched = false;
 		prim.cat = null;
@@ -222,11 +228,10 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 static void loadPrimitiveCatgories() {
 	g_PrimitiveCategories.clear();
-	File f (config::dirpath() + "primregexps.cfg", File::Read);
+	File f (Config::dirpath() + "primregexps.cfg", File::Read);
 	
 	if (!f)
 		f.open (":/data/primitive-categories.cfg", File::Read);
@@ -278,20 +283,19 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 bool primitiveLoaderBusy() {
 	return g_primListerMutex;
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 static double radialPoint (int i, int divs, double (*func) (double)) {
 	return (*func) ((i * 2 * pi) / divs);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 List<LDObject*> makePrimitive (PrimitiveType type, int segs, int divs, int num) {
 	List<LDObject*> objs;
 	List<int> condLineSegs;
@@ -307,7 +311,7 @@
 			vertex v0 (x0, 0.0f, z0),
 				   v1 (x1, 0.0f, z1);
 			
-			LDLineObject* line = new LDLineObject;
+			LDLine* line = new LDLine;
 			line->setVertex (0, v0);
 			line->setVertex (1, v1);
 			line->setColor (edgecolor);
@@ -354,7 +358,7 @@
 					v2 (x2, y2, z2),
 					v3 (x3, y3, z3);
 				
-				LDQuadObject* quad = new LDQuadObject;
+				LDQuad* quad = new LDQuad;
 				quad->setColor (maincolor);
 				quad->setVertex (0, v0);
 				quad->setVertex (1, v1);
@@ -389,7 +393,7 @@
 				
 				// Disc negatives need to go the other way around, otherwise
 				// they'll end up upside-down.
-				LDTriangleObject* seg = new LDTriangleObject;
+				LDTriangle* seg = new LDTriangle;
 				seg->setColor (maincolor);
 				seg->setVertex (type == Disc ? 0 : 2, v0);
 				seg->setVertex (1, v1);
@@ -423,7 +427,7 @@
 			v0[Z] *= num;
 		}
 		
-		LDCondLineObject* line = new LDCondLineObject;
+		LDCndLine* line = new LDCndLine;
 		line->setColor (edgecolor);
 		line->setVertex (0, v0);
 		line->setVertex (1, v1);
@@ -436,8 +440,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 static str primitiveTypeName (PrimitiveType type) {
 	// Not translated as primitives are in English.
 	return type == Circle   ? "Circle" :
@@ -447,18 +450,8 @@
 		type == Ring     ? "Ring" : "Cone";
 }
 
-static const str g_radialNameRoots[] = {
-	"edge",
-	"cyli",
-	"disc",
-	"ndis",
-	"ring",
-	"con"
-};
-
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 str radialFileName (PrimitiveType type, int segs, int divs, int num) {
 	short numer = segs,
 		  denom = divs;
@@ -489,8 +482,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void generatePrimitive() {
 	PrimitivePrompt* dlg = new PrimitivePrompt (g_win);
 	
@@ -532,20 +524,25 @@
 	LDFile* f = new LDFile;
 	f->setName (QFileDialog::getSaveFileName (null, QObject::tr ("Save Primitive"), name));
 	
-	*f << new LDCommentObject (descr);
-	*f << new LDCommentObject (fmt ("Name: %1", name));
-	*f << new LDCommentObject (fmt ("Author: LDForge"));
-	*f << new LDCommentObject (fmt ("!LDRAW_ORG Unofficial_%1Primitive", divs == hires ? "48_" : ""));
-	*f << new LDCommentObject (CALicense);
-	*f << new LDEmptyObject;
-	*f << new LDBFCObject (LDBFCObject::CertifyCCW);
-	*f << new LDEmptyObject;
-	*f << makePrimitive (type, segs, divs, num);
+	f->addObjects ({
+		new LDComment (descr),
+		new LDComment (fmt ("Name: %1", name)),
+		new LDComment (fmt ("Author: LDForge")),
+		new LDComment (fmt ("!LDRAW_ORG Unofficial_%1Primitive", divs == hires ? "48_" : "")),
+		new LDComment (CALicense),
+		new LDEmpty,
+		new LDBFC (LDBFC::CertifyCCW),
+		new LDEmpty,
+	});
+	
+	f->addObjects (makePrimitive (type, segs, divs, num));
 	
 	g_win->save (f, false);
 	delete f;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 PrimitivePrompt::PrimitivePrompt (QWidget* parent, Qt::WindowFlags f) :
 	QDialog (parent, f) {
 	
@@ -554,10 +551,14 @@
 	connect (ui->cb_hires, SIGNAL (toggled(bool)), this, SLOT (hiResToggled (bool)));
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 PrimitivePrompt::~PrimitivePrompt() {
 	delete ui;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void PrimitivePrompt::hiResToggled (bool on) {
 	ui->sb_segs->setMaximum (on ? hires : lores);
 	
@@ -565,6 +566,4 @@
 	// spinbox to 48.
 	if (on && ui->sb_segs->value() == lores)
 		ui->sb_segs->setValue (hires);
-}
-
-#include "build/moc_primitives.cpp"
\ No newline at end of file
+}
\ No newline at end of file
--- a/src/primitives.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/primitives.h	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
--- a/src/types.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/types.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -5,7 +5,7 @@
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ *  (at your option) any later version.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -27,6 +27,8 @@
 #include "ldtypes.h"
 #include "file.h"
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 str DoFormat (List<StringFormatArg> args) {
 	assert (args.size() >= 1);
 	str text = args[0].value();
@@ -37,6 +39,8 @@
 	return text;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 vertex::vertex (double x, double y, double z) {
 	m_coords[X] = x;
 	m_coords[Y] = y;
@@ -44,12 +48,14 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void vertex::move (const vertex& other) {
 	for (const Axis ax : g_Axes)
 		m_coords[ax] += other[ax];
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 vertex vertex::midpoint (const vertex& other) {
 	vertex mid;
 	
@@ -60,6 +66,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 str vertex::stringRep (bool mangled) const {
 	str fmtstr = "%1 %2 %3";
 	if (mangled)
@@ -69,6 +76,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void vertex::transform (matrix matr, vertex pos) {
 	double x2 = (matr[0] * x()) + (matr[1] * y()) + (matr[2] * z()) + pos[X];
 	double y2 = (matr[3] * x()) + (matr[4] * y()) + (matr[5] * z()) + pos[Y];
@@ -79,14 +87,20 @@
 	z() = z2;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 vertex vertex::operator-() const {
 	return vertex (-m_coords[X], -m_coords[Y], -m_coords[Z]);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 bool vertex::operator!= (const vertex& other) const {
 	return !operator== (other);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 double& vertex::operator[] (const Axis ax) {
 	return coord ((ushort) ax);
 }
@@ -103,12 +117,16 @@
 	return coord (ax);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 bool vertex::operator== (const vertex& other) const {
 	return coord (X) == other[X] &&
 	       coord (Y) == other[Y] &&
 	       coord (Z) == other[Z];
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 vertex& vertex::operator/= (const double d) {
 	for (const Axis ax : g_Axes)
 		m_coords[ax] /= d;
@@ -116,22 +134,30 @@
 	return *this;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 vertex vertex::operator/ (const double d) const {
 	vertex other (*this);
 	return other /= d;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 vertex& vertex::operator+= (const vertex& other) {
 	move (other);
 	return *this;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 vertex vertex::operator+ (const vertex& other) const {
 	vertex newvert (*this);
 	newvert.move (other);
 	return newvert;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 int vertex::operator< (const vertex& other) const {
 	if (operator== (other))
 		return false;
@@ -152,21 +178,28 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 matrix::matrix (double vals[]) {
 	for (short i = 0; i < 9; ++i)
 		m_vals[i] = vals[i];
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 matrix::matrix (double fillval) {
 	for (short i = 0; i < 9; ++i)
 		m_vals[i] = fillval;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 matrix::matrix (initlist<double> vals) {
 	assert (vals.size() == 9);
 	memcpy (&m_vals[0], & (*vals.begin()), sizeof m_vals);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void matrix::puts() const {
 	for (short i = 0; i < 3; ++i) {
 		for (short j = 0; j < 3; ++j)
@@ -177,6 +210,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 str matrix::stringRep() const {
 	str val;
 	
@@ -191,11 +225,13 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 void matrix::zero() {
-	memset (&m_vals[0], 0, sizeof (double) * 9);
+	memset (&m_vals[0], 0, sizeof m_vals);
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 matrix matrix::mult (matrix other) const {
 	matrix val;
 	val.zero();
@@ -203,18 +239,20 @@
 	for (short i = 0; i < 3; ++i)
 	for (short j = 0; j < 3; ++j)
 	for (short k = 0; k < 3; ++k)
-		val[ (i * 3) + j] += m_vals[ (i * 3) + k] * other[ (k * 3) + j];
-
+		val[ (i * 3) + j] += m_vals[(i * 3) + k] * other[(k * 3) + j];
+	
 	return val;
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 matrix& matrix::operator= (matrix other) {
-	memcpy (&m_vals[0], &other.m_vals[0], sizeof (double) * 9);
+	memcpy (&m_vals[0], &other.m_vals[0], sizeof m_vals);
 	return *this;
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 double matrix::determinant() const {
 	return (val (0) * val (4) * val (8)) +
 	       (val (1) * val (5) * val (6)) +
@@ -225,6 +263,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 StringFormatArg::StringFormatArg (const str& v) {
 	m_val = v;
 }
@@ -261,15 +300,15 @@
 	m_val = v;
 }
 
-StringFormatArg::StringFormatArg (const strconfig& v) {
+StringFormatArg::StringFormatArg (const StringConfig& v) {
 	m_val = v.value;
 }
 
-StringFormatArg::StringFormatArg (const intconfig& v) {
+StringFormatArg::StringFormatArg (const IntConfig& v) {
 	m_val.number (v.value);
 }
 
-StringFormatArg::StringFormatArg (const floatconfig& v) {
+StringFormatArg::StringFormatArg (const FloatConfig& v) {
 	m_val.number (v.value);
 }
 
@@ -278,6 +317,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 File::File() {
 	// Make a null file
 	m_file = null;
@@ -294,6 +334,8 @@
 	open (fp, rtype);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 File::~File() {
 	if (m_file) {
 		m_file->close();
@@ -304,6 +346,8 @@
 	}
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 bool File::open (FILE* fp, OpenType rtype) {
 	return open ("", rtype, fp);
 }
@@ -337,6 +381,8 @@
 	return false;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 File::iterator File::begin() {
 	return iterator (this);
 }
@@ -345,10 +391,14 @@
 	return m_endIterator;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void File::write (str msg) {
 	m_file->write (msg.toUtf8(), msg.length());
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 bool File::readLine (str& line) {
 	if (!m_textstream || m_textstream->atEnd())
 		return false;
@@ -357,6 +407,8 @@
 	return true;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 bool File::atEnd() const {
 	if (!m_textstream)
 		fatal ("cannot use atEnd on a null file");
@@ -364,6 +416,8 @@
 	return m_textstream->atEnd();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 bool File::isNull() const {
 	return m_file == null;
 }
@@ -372,6 +426,8 @@
 	return isNull();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void File::close() {
 	if (!m_file)
 		return;
@@ -385,76 +441,89 @@
 	}
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 bool File::flush() {
 	return m_file->flush();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 File::operator bool() const {
 	return !isNull();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void File::rewind() {
 	m_file->seek (0);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 File::iterator::iterator (File* f) : m_file (f) {
 	operator++();
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 void File::iterator::operator++() {
 	m_gotdata = m_file->readLine (m_text);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 str File::iterator::operator*() {
 	return m_text;
 }
 
+// =============================================================================
 // The prime contestant for the weirdest operator== 2013 award?
+// -----------------------------------------------------------------------------
 bool File::iterator::operator== (File::iterator& other) {
 	return (other.m_file == null && !m_gotdata);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 bool File::iterator::operator!= (File::iterator& other) {
 	return !operator== (other);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 LDBoundingBox::LDBoundingBox() {
 	reset();
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void LDBoundingBox::calculate() {
 	reset();
 	
 	if (!LDFile::current())
 		return;
 	
-	for (LDObject* obj : LDFile::current()->objs())
+	for (LDObject* obj : LDFile::current()->objects())
 		calcObject (obj);
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void LDBoundingBox::calcObject (LDObject* obj) {
 	switch (obj->getType()) {
 	case LDObject::Line:
 	case LDObject::Triangle:
 	case LDObject::Quad:
-	case LDObject::CondLine:
+	case LDObject::CndLine:
 		for (short i = 0; i < obj->vertices(); ++i)
 			calcVertex (obj->getVertex (i));
 		
 		break;
 
 	case LDObject::Subfile: {
-		LDSubfileObject* ref = static_cast<LDSubfileObject*> (obj);
-		List<LDObject*> objs = ref->inlineContents (true, true);
+		LDSubfile* ref = static_cast<LDSubfile*> (obj);
+		List<LDObject*> objs = ref->inlineContents (LDSubfile::DeepCacheInline);
 	
 		for (LDObject* obj : objs) {
 			calcObject (obj);
@@ -468,19 +537,22 @@
 	}
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 LDBoundingBox& LDBoundingBox::operator<< (const vertex& v) {
 	calcVertex (v);
 	return *this;
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 LDBoundingBox& LDBoundingBox::operator<< (LDObject* obj) {
 	calcObject (obj);
 	return *this;
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void LDBoundingBox::calcVertex (const vertex& v) {
 	for (const Axis ax : g_Axes) {
 		if (v[ax] < m_v0[ax])
@@ -494,8 +566,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 void LDBoundingBox::reset() {
 	m_v0[X] = m_v0[Y] = m_v0[Z] = 0x7FFFFFFF;
 	m_v1[X] = m_v1[Y] = m_v1[Z] = 0xFFFFFFFF;
@@ -504,8 +575,7 @@
 }
 
 // =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
+// -----------------------------------------------------------------------------
 double LDBoundingBox::size() const {
 	double xscale = (m_v0[X] - m_v1[X]);
 	double yscale = (m_v0[Y] - m_v1[Y]);
@@ -525,6 +595,7 @@
 }
 
 // =============================================================================
+// -----------------------------------------------------------------------------
 vertex LDBoundingBox::center() const {
 	return vertex (
 		(m_v0[X] + m_v1[X]) / 2,
--- a/src/types.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/types.h	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -30,9 +30,9 @@
 typedef QString str;
 template<class T> class ConstListReverser;
 template<class T> using c_rev = ConstListReverser<T>;
-class strconfig;
-class intconfig;
-class floatconfig;
+class StringConfig;
+class IntConfig;
+class FloatConfig;
 class QFile;
 class QTextStream;
 
@@ -279,6 +279,11 @@
 	std::deque<T> m_vect;
 };
 
+template<class T> static inline T& operator>> (const T& a, List<T>& b) {
+	b.insert (0, a);
+	return b;
+}
+
 // =============================================================================
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
@@ -370,9 +375,9 @@
 	StringFormatArg (const vertex& v);
 	StringFormatArg (const matrix& v);
 	StringFormatArg (const char* v);
-	StringFormatArg (const strconfig& v);
-	StringFormatArg (const intconfig& v);
-	StringFormatArg (const floatconfig& v);
+	StringFormatArg (const StringConfig& v);
+	StringFormatArg (const IntConfig& v);
+	StringFormatArg (const FloatConfig& v);
 	StringFormatArg (const void* v);
 	
 	template<class T> StringFormatArg (const List<T>& v) {
@@ -509,6 +514,6 @@
 extern const vertex g_origin; // Vertex at (0, 0, 0)
 extern const matrix g_identity; // Identity matrix
 
-static const double pi = 3.14159265358979323846f;
+static const double pi = 3.14159265358979323846;
 
 #endif // TYPES_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ui/addhistoryline.ui	Sat Sep 07 13:23:09 2013 +0300
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AddHistoryLine</class>
+ <widget class="QDialog" name="AddHistoryLine">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>410</width>
+    <height>120</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Add History Line</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QFormLayout" name="formLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Date:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QDateEdit" name="m_date"/>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Username:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QLineEdit" name="m_username"/>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>Comment:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLineEdit" name="m_comment"/>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>AddHistoryLine</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>AddHistoryLine</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- a/src/ui/config.ui	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/ui/config.ui	Sat Sep 07 13:23:09 2013 +0300
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>513</width>
-    <height>377</height>
+    <width>561</width>
+    <height>351</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -24,7 +24,7 @@
       <enum>QTabWidget::North</enum>
      </property>
      <property name="currentIndex">
-      <number>5</number>
+      <number>0</number>
      </property>
      <property name="elideMode">
       <enum>Qt::ElideNone</enum>
@@ -40,7 +40,7 @@
      </property>
      <widget class="QWidget" name="tab">
       <attribute name="title">
-       <string>General</string>
+       <string>Interface</string>
       </attribute>
       <layout class="QVBoxLayout" name="verticalLayout_2">
        <item>
@@ -190,40 +190,157 @@
         </layout>
        </item>
        <item>
-        <widget class="QCheckBox" name="colorizeObjects">
-         <property name="whatsThis">
-          <string>Makes colored objects (non-16 and 24) appear colored in the list view. A red triangle will, for instance, have its entry written in red text. This can be useful to locate colored objects.</string>
-         </property>
-         <property name="text">
-          <string>Colorize objects in list view</string>
-         </property>
-        </widget>
+        <layout class="QHBoxLayout" name="horizontalLayout_8">
+         <item>
+          <layout class="QVBoxLayout" name="verticalLayout_11">
+           <item>
+            <widget class="QCheckBox" name="blackEdges">
+             <property name="whatsThis">
+              <string>Makes all edgelines appear black. If this is not set, edge lines take their color as defined in LDConfig.ldr.</string>
+             </property>
+             <property name="text">
+              <string>Black edges</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QCheckBox" name="colorBFC">
+             <property name="whatsThis">
+              <string>Polygons' front sides become green and back sides red.</string>
+             </property>
+             <property name="text">
+              <string>Red/green BFC view (incomplete)</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QCheckBox" name="colorizeObjects">
+             <property name="whatsThis">
+              <string>Makes colored objects (non-16 and 24) appear colored in the list view. A red triangle will, for instance, have its entry written in red text. This can be useful to locate colored objects.</string>
+             </property>
+             <property name="text">
+              <string>Colorize objects in list view</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <widget class="QCheckBox" name="implicitFiles">
+             <property name="text">
+              <string>List implicitly loaded files</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </item>
+         <item>
+          <layout class="QVBoxLayout" name="verticalLayout_12">
+           <item>
+            <widget class="QCheckBox" name="m_logostuds">
+             <property name="text">
+              <string>Use logoed studs</string>
+             </property>
+            </widget>
+           </item>
+           <item>
+            <spacer name="verticalSpacer_6">
+             <property name="orientation">
+              <enum>Qt::Vertical</enum>
+             </property>
+             <property name="sizeHint" stdset="0">
+              <size>
+               <width>20</width>
+               <height>40</height>
+              </size>
+             </property>
+            </spacer>
+           </item>
+          </layout>
+         </item>
+        </layout>
        </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tab_7">
+      <attribute name="title">
+       <string>Profile</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_10">
        <item>
-        <widget class="QCheckBox" name="colorBFC">
-         <property name="whatsThis">
-          <string>Polygons' front sides become green and back sides red.</string>
-         </property>
-         <property name="text">
-          <string>Red/green BFC view (incomplete)</string>
+        <widget class="QGroupBox" name="groupBox_4">
+         <property name="title">
+          <string>Profile</string>
          </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QCheckBox" name="blackEdges">
-         <property name="whatsThis">
-          <string>Makes all edgelines appear black. If this is not set, edge lines take their color as defined in LDConfig.ldr.</string>
-         </property>
-         <property name="text">
-          <string>Black edges</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <widget class="QCheckBox" name="implicitFiles">
-         <property name="text">
-          <string>List implicitly loaded files</string>
-         </property>
+         <layout class="QVBoxLayout" name="verticalLayout_8">
+          <item>
+           <layout class="QFormLayout" name="formLayout_3">
+            <item row="2" column="0">
+             <widget class="QLabel" name="label_8">
+              <property name="text">
+               <string>Username:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="0">
+             <widget class="QLabel" name="label_7">
+              <property name="text">
+               <string>Name:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="0">
+             <widget class="QLabel" name="label_9">
+              <property name="text">
+               <string>License:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="1">
+             <widget class="QLineEdit" name="m_profileName"/>
+            </item>
+            <item row="2" column="1">
+             <widget class="QLineEdit" name="m_profileUsername"/>
+            </item>
+            <item row="3" column="1">
+             <widget class="QComboBox" name="m_profileLicense">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+                <horstretch>0</horstretch>
+                <verstretch>0</verstretch>
+               </sizepolicy>
+              </property>
+              <item>
+               <property name="text">
+                <string>CA - redistributable</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>NonCA - not redistributable</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>None</string>
+               </property>
+              </item>
+             </widget>
+            </item>
+           </layout>
+          </item>
+          <item>
+           <spacer name="verticalSpacer_7">
+            <property name="orientation">
+             <enum>Qt::Vertical</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>20</width>
+              <height>40</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+         </layout>
         </widget>
        </item>
       </layout>
@@ -574,7 +691,7 @@
       <enum>Qt::Horizontal</enum>
      </property>
      <property name="standardButtons">
-      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+      <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
      </property>
     </widget>
    </item>
@@ -583,38 +700,5 @@
  <resources>
   <include location="../../ldforge.qrc"/>
  </resources>
- <connections>
-  <connection>
-   <sender>buttonBox</sender>
-   <signal>accepted()</signal>
-   <receiver>ConfigUI</receiver>
-   <slot>accept()</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>248</x>
-     <y>254</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>157</x>
-     <y>274</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>buttonBox</sender>
-   <signal>rejected()</signal>
-   <receiver>ConfigUI</receiver>
-   <slot>reject()</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>316</x>
-     <y>260</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>286</x>
-     <y>274</y>
-    </hint>
-   </hints>
-  </connection>
- </connections>
+ <connections/>
 </ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ui/extprogpath.ui	Sat Sep 07 13:23:09 2013 +0300
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ExtProgPath</class>
+ <widget class="QDialog" name="ExtProgPath">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>444</width>
+    <height>89</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Program path required</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QLabel" name="m_label">
+     <property name="text">
+      <string>Please input a path for &lt;PROGRAM&gt;:</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLineEdit" name="m_path"/>
+     </item>
+     <item>
+      <widget class="QPushButton" name="m_findPath">
+       <property name="text">
+        <string>...</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>ExtProgPath</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>ExtProgPath</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- a/src/ui/ldforge.ui	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/ui/ldforge.ui	Sat Sep 07 13:23:09 2013 +0300
@@ -70,7 +70,7 @@
      <x>0</x>
      <y>0</y>
      <width>900</width>
-     <height>26</height>
+     <height>22</height>
     </rect>
    </property>
    <widget class="QMenu" name="menuFile">
@@ -159,6 +159,8 @@
     <addaction name="actionModeDraw"/>
     <addaction name="separator"/>
     <addaction name="actionSetDrawDepth"/>
+    <addaction name="separator"/>
+    <addaction name="actionJumpTo"/>
    </widget>
    <widget class="QMenu" name="menuTools">
     <property name="title">
@@ -173,6 +175,8 @@
     <addaction name="actionInlineDeep"/>
     <addaction name="actionMakePrimitive"/>
     <addaction name="separator"/>
+    <addaction name="actionAddHistoryLine"/>
+    <addaction name="separator"/>
     <addaction name="actionSplitQuads"/>
     <addaction name="actionEditRaw"/>
     <addaction name="actionBorders"/>
@@ -1264,6 +1268,16 @@
     <string>Download From...</string>
    </property>
   </action>
+  <action name="actionAddHistoryLine">
+   <property name="text">
+    <string>Add History Line</string>
+   </property>
+  </action>
+  <action name="actionJumpTo">
+   <property name="text">
+    <string>Go to Line...</string>
+   </property>
+  </action>
  </widget>
  <resources>
   <include location="../../ldforge.qrc"/>
--- a/src/widgets.cpp	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/widgets.cpp	Sat Sep 07 13:23:09 2013 +0300
@@ -1,21 +1,25 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+// I still find the radio group useful... find a way to use this in Designer.
+// I probably need to look into how to make Designer plugins.
+// TODO: try make this usable in Designer
+
 #include <QBoxLayout>
 #include <QRadioButton>
 #include <QButtonGroup>
@@ -24,19 +28,27 @@
 
 #include "widgets.h"
 
-RadioBox::RadioBox (const QString& title, QWidget* parent) : QGroupBox (title, parent) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+RadioGroup::RadioGroup (const QString& title, QWidget* parent) : QGroupBox (title, parent) {
 	init (Qt::Vertical);
 }
 
+// =============================================================================
+// -----------------------------------------------------------------------------
 QBoxLayout::Direction makeDirection (Qt::Orientation orient, bool invert = false) {
 	return (orient == (invert ? Qt::Vertical : Qt::Horizontal)) ? QBoxLayout::LeftToRight : QBoxLayout::TopToBottom;
 }
 
-bool RadioBox::isChecked (int n) const {
+// =============================================================================
+// -----------------------------------------------------------------------------
+bool RadioGroup::isChecked (int n) const {
 	return m_buttonGroup->checkedId() == n;
 }
 
-void RadioBox::init (Qt::Orientation orient) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+void RadioGroup::init (Qt::Orientation orient) {
 	m_vert = orient == Qt::Vertical;
 	
 	m_buttonGroup = new QButtonGroup;
@@ -53,7 +65,9 @@
 	connect (m_buttonGroup, SIGNAL (buttonReleased (int)), this, SLOT (slot_buttonReleased (int)));
 }
 
-RadioBox::RadioBox (const QString& title, initlist<char const*> entries, int const defaultId,
+// =============================================================================
+// -----------------------------------------------------------------------------
+RadioGroup::RadioGroup (const QString& title, initlist<char const*> entries, int const defaultId,
 	const Qt::Orientation orient, QWidget* parent) : QGroupBox (title, parent), m_defId (defaultId)
 {
 	init (orient);
@@ -63,7 +77,9 @@
 		addButton (entry);
 }
 
-void RadioBox::rowBreak() {
+// =============================================================================
+// -----------------------------------------------------------------------------
+void RadioGroup::rowBreak() {
 	QBoxLayout* newLayout = new QBoxLayout (m_vert ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight);
 	m_currentLayout = newLayout;
 	m_layouts << newLayout;
@@ -71,12 +87,16 @@
 	m_coreLayout->addLayout (newLayout);
 }
 
-void RadioBox::addButton (const char* entry) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+void RadioGroup::addButton (const char* entry) {
 	QRadioButton* button = new QRadioButton (entry);
 	addButton (button);
 }
 
-void RadioBox::addButton (QRadioButton* button) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+void RadioGroup::addButton (QRadioButton* button) {
 	bool const selectThis = (m_curId == m_defId);
 	
 	m_objects << button;
@@ -87,39 +107,55 @@
 		button->setChecked (true);
 }
 
-RadioBox& RadioBox::operator<< (QRadioButton* button) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+RadioGroup& RadioGroup::operator<< (QRadioButton* button) {
 	addButton (button);
 	return *this;
 }
 
-RadioBox& RadioBox::operator<< (const char* entry) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+RadioGroup& RadioGroup::operator<< (const char* entry) {
 	addButton (entry);
 	return *this;
 }
 
-void RadioBox::setCurrentRow (uint row) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+void RadioGroup::setCurrentRow (uint row) {
 	m_currentLayout = m_layouts[row];
 }
 
-int RadioBox::value() const {
+// =============================================================================
+// -----------------------------------------------------------------------------
+int RadioGroup::value() const {
 	return m_buttonGroup->checkedId();
 }
 
-void RadioBox::setValue (int val) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+void RadioGroup::setValue (int val) {
 	m_buttonGroup->button (val)->setChecked (true);
 }
 
-QRadioButton* RadioBox::operator[] (uint n) const {
+// =============================================================================
+// -----------------------------------------------------------------------------
+QRadioButton* RadioGroup::operator[] (uint n) const {
 	return m_objects[n];
 }
 
-void RadioBox::slot_buttonPressed (int btn) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+void RadioGroup::slot_buttonPressed (int btn) {
 	emit buttonPressed (btn);
 	
 	m_oldId = m_buttonGroup->checkedId();
 }
 
-void RadioBox::slot_buttonReleased (int btn) {
+// =============================================================================
+// -----------------------------------------------------------------------------
+void RadioGroup::slot_buttonReleased (int btn) {
 	emit buttonReleased (btn);
 	int newid = m_buttonGroup->checkedId();
 	
@@ -127,12 +163,14 @@
 		emit valueChanged (newid);
 }
 
-RadioBox::it RadioBox::begin() {
+// =============================================================================
+// -----------------------------------------------------------------------------
+RadioGroup::it RadioGroup::begin() {
 	 return m_objects.begin();
 }
 
-RadioBox::it RadioBox::end() {
+// =============================================================================
+// -----------------------------------------------------------------------------
+RadioGroup::it RadioGroup::end() {
 	return m_objects.end();
-}
-
-#include "build/moc_widgets.cpp"
\ No newline at end of file
+}
\ No newline at end of file
--- a/src/widgets.h	Fri Aug 16 11:05:21 2013 +0300
+++ b/src/widgets.h	Sat Sep 07 13:23:09 2013 +0300
@@ -1,17 +1,17 @@
 /*
  *  LDForge: LDraw parts authoring CAD
  *  Copyright (C) 2013 Santeri Piippo
- *  
+ *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation, either version 3 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -25,26 +25,27 @@
 #include "common.h"
 #include "types.h"
 
+class QIcon;
 class QCheckBox;
 class QButtonGroup;
 class QBoxLayout;
 class QRadioButton;
 
 // =============================================================================
-// RadioBox
+// RadioGroup
 //
 // Convenience widget - is a groupbox of radio buttons.
 // =============================================================================
-class RadioBox : public QGroupBox {
+class RadioGroup : public QGroupBox {
 	Q_OBJECT
 	
 public:
 	typedef List<QRadioButton*>::it it;
 	
-	explicit RadioBox() { init (Qt::Vertical); }
-	explicit RadioBox (QWidget* parent = null) : QGroupBox (parent) { init (Qt::Vertical); }
-	explicit RadioBox (const QString& title, QWidget* parent = null);
-	explicit RadioBox (const QString& title, initlist<char const*> entries, int const defaultId,
+	explicit RadioGroup() { init (Qt::Vertical); }
+	explicit RadioGroup (QWidget* parent = null) : QGroupBox (parent) { init (Qt::Vertical); }
+	explicit RadioGroup (const QString& title, QWidget* parent = null);
+	explicit RadioGroup (const QString& title, initlist<char const*> entries, int const defaultId,
 		const Qt::Orientation orient = Qt::Vertical, QWidget* parent = null);
 	
 	void			addButton		(const char* entry);
@@ -59,8 +60,8 @@
 	int				value			() const;
 	
 	QRadioButton*	operator[]		(uint n) const;
-	RadioBox&		operator<<		(QRadioButton* button);
-	RadioBox&		operator<<		(const char* entry);
+	RadioGroup&		operator<<		(QRadioButton* button);
+	RadioGroup&		operator<<		(const char* entry);
 
 signals:
 	void buttonPressed (int btn);
@@ -76,7 +77,7 @@
 	int m_curId, m_defId, m_oldId;
 	QButtonGroup* m_buttonGroup;
 	
-	Q_DISABLE_COPY (RadioBox)
+	Q_DISABLE_COPY (RadioGroup)
 
 private slots:
 	void slot_buttonPressed (int btn);

mercurial