Added LDraw path setting dialog

Mon, 18 Mar 2013 18:29:02 +0200

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Mon, 18 Mar 2013 18:29:02 +0200
changeset 29
55406ce7446e
parent 28
208adc847377
child 30
31ff9aabd506

Added LDraw path setting dialog

cfgdef.h file | annotate | diff | comparison | revisions
color.h file | annotate | diff | comparison | revisions
common.h file | annotate | diff | comparison | revisions
config.cpp file | annotate | diff | comparison | revisions
config.h file | annotate | diff | comparison | revisions
file.cpp file | annotate | diff | comparison | revisions
file.h file | annotate | diff | comparison | revisions
gui.cpp file | annotate | diff | comparison | revisions
gui.h file | annotate | diff | comparison | revisions
icons/clips/folder.png file | annotate | diff | comparison | revisions
icons/file-open.png file | annotate | diff | comparison | revisions
icons/folder.png file | annotate | diff | comparison | revisions
ldforge.pro file | annotate | diff | comparison | revisions
ldtypes.cpp file | annotate | diff | comparison | revisions
ldtypes.h file | annotate | diff | comparison | revisions
main.cpp file | annotate | diff | comparison | revisions
misc.cpp file | annotate | diff | comparison | revisions
misc.h file | annotate | diff | comparison | revisions
str.cpp file | annotate | diff | comparison | revisions
str.h file | annotate | diff | comparison | revisions
zz_configDialog.cpp file | annotate | diff | comparison | revisions
zz_configDialog.h file | annotate | diff | comparison | revisions
zz_setContentsDialog.cpp file | annotate | diff | comparison | revisions
--- a/cfgdef.h	Mon Mar 18 13:16:21 2013 +0200
+++ b/cfgdef.h	Mon Mar 18 18:29:02 2013 +0200
@@ -3,4 +3,4 @@
 
 // ---------------------------------------------------------
 SECT (io, Files)
-CFG (str, io, openfile, "Current open fle", "box5.dat")
\ No newline at end of file
+CFG (str, io, ldpath, "LDraw path", "")
\ No newline at end of file
--- a/color.h	Mon Mar 18 13:16:21 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,75 +0,0 @@
-#ifndef __COLOR_H__
-#define __COLOR_H__
-
-#include <stdint.h>
-
-#ifdef QT_VERSION
- #include <QColor>
-#endif // QT_VERSION
-
-class color {
-public:
-	unsigned char r, g, b;
-	
-	color () {}
-	color (const char* other) {
-		parseFromString (str (other));
-	}
-	
-	bool parseFromString (str in) {
-#ifdef QT_VERSION
-		// Use Qt's method for a quick color check. Handles
-		// named colors, too.
-		QColor col (in.chars ());
-		if (col.isValid()) {
-			r = col.red ();
-			g = col.green ();
-			b = col.blue ();
-			return true;
-		}
-#else
-		if (in[0] == '#') {
-			// Hex code
-			
-			if (~in != 4 && ~in != 7)
-				return false; // bad length
-			
-			printf ("%s\n", in.chars ());
-			
-			if (~in == 4) {
-				in.format ("#%c%c%c%c%c%c",
-					in[1], in[1],
-					in[2], in[2],
-					in[3], in[3]);
-			}
-			
-			printf ("%s\n", in.chars ());
-			
-			if (sscanf (in.chars(), "#%2hhx%2hhx%2hhx", &r, &g, &b))
-				return true;
-		}
-#endif // QT_VERSION
-		
-		return false;
-	}
-	
-	str toString () {
-		str val;
-		val.format ("#%.2X%.2X%.2X", r, g, b);
-		return val;
-	}
-	
-	char* chars() {
-		return toString ().chars ();
-	}
-	
-	operator str () {
-		return toString ();
-	}
-	
-	void operator= (const str other) {
-		parseFromString (other);
-	}
-};
-
-#endif // __COLOR_H__
\ No newline at end of file
--- a/common.h	Mon Mar 18 13:16:21 2013 +0200
+++ b/common.h	Mon Mar 18 18:29:02 2013 +0200
@@ -29,6 +29,12 @@
  #define FORMAT_PRINTF(M,N)
 #endif // __GNUC__
 
+#ifdef WIN32
+#define DIRSLASH "\\"
+#else // WIN32
+#define DIRSLASH "/"
+#endif // WIN32
+
 using std::vector;
 
 class ForgeWindow;
--- a/config.cpp	Mon Mar 18 13:16:21 2013 +0200
+++ b/config.cpp	Mon Mar 18 18:29:02 2013 +0200
@@ -80,7 +80,6 @@
 	"String",
 	"Float",
 	"Boolean",
-	"Color",
 };
 
 // =============================================================================
@@ -186,9 +185,6 @@
 				val = false;
 			break;
 		}
-		case CONFIG_color:
-			static_cast<colorconfig*> (cfg)->value.parseFromString (valstring);
-			break;
 		default:
 			break;
 		}
@@ -295,9 +291,6 @@
 			case CONFIG_bool:
 				valstring = (static_cast<boolconfig*> (cfg)->value) ? "true" : "false";
 				break;
-			case CONFIG_color:
-				valstring = (str)(static_cast<colorconfig*> (cfg)->value);
-				break;
 			default:
 				break;
 			}
--- a/config.h	Mon Mar 18 13:16:21 2013 +0200
+++ b/config.h	Mon Mar 18 18:29:02 2013 +0200
@@ -3,7 +3,6 @@
 
 #include "common.h"
 #include "str.h"
-#include "color.h"
 
 // =============================================================================
 // Determine configuration file. Use APPNAME if given.
@@ -42,7 +41,6 @@
 	CONFIG_str,
 	CONFIG_float,
 	CONFIG_bool,
-	CONFIG_color,
 };
 
 // =========================================================
@@ -220,14 +218,6 @@
 };
 
 // =============================================================================
-CONFIGTYPE (color) {
-public:
-	IMPLEMENT_CONFIG (color)
-	// DEFINE_COMPARE_OPERATOR (color, ==)
-	// DEFINE_COMPARE_OPERATOR (color, !=)
-};
-
-// =============================================================================
 // Extern the configurations now
 #define CFG(TYPE, SECT, NAME, DESCR, DEFAULT) extern TYPE##config SECT##_##NAME;
 #define SECT(...)
--- a/file.cpp	Mon Mar 18 13:16:21 2013 +0200
+++ b/file.cpp	Mon Mar 18 18:29:02 2013 +0200
@@ -6,18 +6,20 @@
 #include "bbox.h"
 #include "gui.h"
 
-vector<str> g_zaFileLoadPaths;
-
 // =============================================================================
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
-OpenFile* findLoadedFile (str name) {
+OpenFile* findLoadedFile (str zName) {
+	printf ("finding %s in files\n", zName.chars());
+	
 	for (ulong i = 0; i < g_LoadedFiles.size(); i++) {
 		OpenFile* const file = g_LoadedFiles[i];
-		if (file->zFileName == name)
+		printf ("%s <-> %s\n", file->zFileName.chars(), zName.chars());
+		if (file->zFileName == zName)
 			return file;
 	}
 	
+	printf ("failed to find given file\n");
 	return nullptr;
 }
 
@@ -29,9 +31,17 @@
 	
 	FILE* fp = fopen (path.chars (), "r");
 	
-	if (!fp) {
-		for (ushort i = 0; i < g_zaFileLoadPaths.size(); ++i) {
-			str zFilePath = str::mkfmt ("%s/%s", g_zaFileLoadPaths[i].chars(), path.chars());
+	if (!fp && ~io_ldpath.value) {
+		char const* saSubdirectories[] = {
+			"parts",
+			"p",
+		};
+		
+		for (ushort i = 0; i < sizeof saSubdirectories / sizeof *saSubdirectories; ++i) {
+			str zFilePath = str::mkfmt ("%s" DIRSLASH "%s" DIRSLASH "%s",
+				io_ldpath.value.chars(), saSubdirectories[i], path.chars());
+			
+			printf ("trying %s...\n", zFilePath.chars ());
 			fp = fopen (zFilePath.chars (), "r");
 			
 			if (fp)
@@ -114,7 +124,7 @@
 	g_LoadedFiles.clear();
 	g_CurrentFile = NULL;
 	
-	g_qWindow->R->hardRefresh();
+	g_qWindow->refresh ();
 }
 
 // =============================================================================
@@ -130,8 +140,7 @@
 	g_CurrentFile = f;
 	
 	g_BBox.calculate();
-	g_qWindow->buildObjList ();
-	g_qWindow->R->hardRefresh();
+	g_qWindow->refresh ();
 }
 
 // =============================================================================
@@ -147,8 +156,7 @@
 	g_BBox.calculate();
 	
 	// Rebuild the object tree view now.
-	g_qWindow->buildObjList ();
-	g_qWindow->setTitle ();
+	g_qWindow->refresh ();
 }
 
 // =============================================================================
@@ -227,9 +235,7 @@
 #endif // WIN32
 			
 			// Try open the file
-			OpenFile* pFile = findLoadedFile (tokens[14]);
-			if (!pFile)
-				pFile = openDATFile (tokens[14]);
+			OpenFile* pFile = loadSubfile (tokens[14]);
 			
 			// If we cannot open the file, mark it an error
 			if (!pFile)
@@ -310,4 +316,52 @@
 	default: // Strange line we couldn't parse
 		return new LDGibberish (zLine, "Unknown line code number");
 	}
+}
+
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
+OpenFile* loadSubfile (str zFile) {
+	// Try open the file
+	OpenFile* pFile = findLoadedFile (zFile);
+	if (!pFile)
+		pFile = openDATFile (zFile);
+	
+	return pFile;
+}
+
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
+void reloadAllSubfiles () {
+	// First, close all but the current open file.
+	for (ushort i = 0; i < g_LoadedFiles.size(); ++i)
+		if (g_LoadedFiles[i] != g_CurrentFile)
+			g_LoadedFiles[i]->close();
+	
+	g_LoadedFiles.clear ();
+	g_LoadedFiles.push_back (g_CurrentFile);
+	
+	// Go through all the current file and reload the subfiles
+	for (ulong i = 0; i < g_CurrentFile->objects.size(); ++i) {
+		LDObject* obj = g_CurrentFile->objects[i];
+		
+		// Reload subfiles
+		if (obj->getType() == OBJ_Subfile) {
+			LDSubfile* ref = static_cast<LDSubfile*> (obj);
+			OpenFile* pFile = loadSubfile (ref->zFileName);
+			
+			if (pFile)
+				ref->pFile = pFile;
+			else {
+				// Couldn't load the file, mark it an error
+				ref->replace (new LDGibberish (ref->getContents (), "Could not open referred file"));
+			}
+		}
+		
+		// Reparse gibberish files. It could be that they are invalid because
+		// the file could not be opened. Circumstances may be different now.
+		if (obj->getType() == OBJ_Gibberish)
+			obj->replace (parseLine (static_cast<LDGibberish*> (obj)->zContents));
+	}
 }
\ No newline at end of file
--- a/file.h	Mon Mar 18 13:16:21 2013 +0200
+++ b/file.h	Mon Mar 18 18:29:02 2013 +0200
@@ -42,7 +42,12 @@
 // Parses a string line containing an LDraw object and returns the object parsed.
 LDObject* parseLine (str zLine);
 
-extern vector<str> g_zaFileLoadPaths;
+// Retrieves the pointer to - or loads - the given subfile.
+OpenFile* loadSubfile (str zFile);
+
+// Re-caches all subfiles.
+void reloadAllSubfiles ();
+
 extern vector<OpenFile*> g_LoadedFiles;
 
 #endif // __FILE_H__
\ No newline at end of file
--- a/gui.cpp	Mon Mar 18 13:16:21 2013 +0200
+++ b/gui.cpp	Mon Mar 18 18:29:02 2013 +0200
@@ -6,6 +6,7 @@
 #include "file.h"
 
 #include "zz_setContentsDialog.h"
+#include "zz_configDialog.h"
 
 ForgeWindow::ForgeWindow () {
 	R = new renderer;
@@ -36,6 +37,7 @@
 	
 	slot_selectionChanged ();
 	
+	setWindowIcon (QIcon ("icons/ldforge.png"));
 	setTitle ();
 	setMinimumSize (320, 200);
 	resize (800, 600);
@@ -46,9 +48,12 @@
 	qAct_##OBJECT->setStatusTip (tr (DESCR)); \
 	connect (qAct_##OBJECT, SIGNAL (triggered ()), this, SLOT (slot_##OBJECT ()));
 
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
 void ForgeWindow::createMenuActions () {
 	// Long menu names go here so my cool action definition table doesn't get out of proportions
-	const char* sNewCdLineText = "New Conditional Line",
+	char const* sNewCdLineText = "New Conditional Line",
 		*sNewQuadText = "New Quadrilateral",
 		*sAboutText = "About " APPNAME_DISPLAY;
 	
@@ -61,8 +66,6 @@
 	MAKE_ACTION (cut,			"Cut",			"cut",			"Cut the current selection to clipboard.")
 	MAKE_ACTION (copy,			"Copy",			"copy",			"Copy the current selection to clipboard.")
 	MAKE_ACTION (paste,			"Paste",		"paste",		"Paste clipboard contents.")
-	MAKE_ACTION (about,			sAboutText,		"about",		"Shows information about " APPNAME_DISPLAY ".")
-	MAKE_ACTION (aboutQt,		"About Qt",		"aboutQt",		"Shows information about Qt.")
 	
 	MAKE_ACTION (inline,		"Inline",		"inline",		"Inline selected subfiles.")
 	MAKE_ACTION (splitQuads,	"Split Quads",	"quad-split",	"Split quads into triangles.")
@@ -77,6 +80,11 @@
 	MAKE_ACTION (newVector,		"New Vector",	"add-vector",	"Creates a new vector.")
 	MAKE_ACTION (newVertex,		"New Vertex",	"add-vertex",	"Creates a new vertex.")
 	
+	MAKE_ACTION (settings,		"Settings",		"settings",		"Edit the settings of " APPNAME_DISPLAY ".")
+	
+	MAKE_ACTION (about,			sAboutText,		"ldforge",		"Shows information about " APPNAME_DISPLAY ".")
+	MAKE_ACTION (aboutQt,		"About Qt",		"aboutQt",		"Shows information about Qt.")
+	
 	// Keyboard shortcuts
 	qAct_new->setShortcut (Qt::CTRL | Qt::Key_N);
 	qAct_open->setShortcut (Qt::CTRL | Qt::Key_O);
@@ -107,6 +115,9 @@
 		qaDisabledActions[i]->setEnabled (false);
 }
 
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
 void ForgeWindow::createMenus () {
 	// File menu
 	qFileMenu = menuBar ()->addMenu (tr ("&File"));
@@ -115,6 +126,8 @@
 	qFileMenu->addAction (qAct_save);			// Save
 	qFileMenu->addAction (qAct_saveAs);			// Save As
 	qFileMenu->addSeparator ();					// -------
+	qFileMenu->addAction (qAct_settings);		// Settings
+	qFileMenu->addSeparator ();					// -------
 	qFileMenu->addAction (qAct_exit);			// Exit
 	
 	// Edit menu
@@ -143,6 +156,9 @@
 	qHelpMenu->addAction (qAct_aboutQt);		// About Qt
 }
 
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
 void ForgeWindow::createToolbars () {
 	qFileToolBar = new QToolBar ("File");
 	qFileToolBar->addAction (qAct_new);
@@ -172,6 +188,9 @@
 	addToolBar (qEditToolBar);
 }
 
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
 void ForgeWindow::setTitle () {
 	str zTitle = APPNAME_DISPLAY " v" VERSION_STRING;
 	
@@ -217,6 +236,10 @@
 		g_CurrentFile->zFileName = zName;
 }
 
+void ForgeWindow::slot_settings () {
+	ConfigDialog::staticDialog (this);
+}
+
 void ForgeWindow::slot_exit () {
 	exit (0);
 }
@@ -290,7 +313,7 @@
 	if (qObjList->selectedItems().size() == 0)
 		return;
 	
-	const QList<QTreeWidgetItem*> qaItems = qObjList->selectedItems();
+	QList<QTreeWidgetItem*> const qaItems = qObjList->selectedItems();
 	
 	for (ulong i = 0; i < g_CurrentFile->objects.size(); ++i) {
 		LDObject* obj = g_CurrentFile->objects[i];
@@ -343,6 +366,9 @@
 	Dialog_SetContents::staticDialog (obj, this);
 }
 
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
 static QIcon IconForObjectType (LDObject* obj) {
 	switch (obj->getType ()) {
 	case OBJ_Empty:
@@ -380,6 +406,9 @@
 	return QIcon ();
 }
 
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
 void ForgeWindow::buildObjList () {
 	if (!g_CurrentFile)
 		return;
@@ -471,6 +500,9 @@
 	qObjList->insertTopLevelItems (0, qaItems);
 }
 
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
 void ForgeWindow::slot_selectionChanged () {
 	// If the selection isn't 1 exact, disable setting contents
 	qAct_setContents->setEnabled (qObjList->selectedItems().size() == 1);
@@ -499,4 +531,9 @@
 	
 	// Otherwise place the object at the end.
 	return g_CurrentFile->objects.size();
+}
+
+void ForgeWindow::refresh () {
+	buildObjList ();
+	R->hardRefresh ();
 }
\ No newline at end of file
--- a/gui.h	Mon Mar 18 13:16:21 2013 +0200
+++ b/gui.h	Mon Mar 18 18:29:02 2013 +0200
@@ -35,11 +35,13 @@
 	QAction* qAct_newSubfile, *qAct_newLine, *qAct_newTriangle, *qAct_newQuad;
 	QAction* qAct_newCondLine, *qAct_newComment, *qAct_newVector, *qAct_newVertex;
 	QAction* qAct_splitQuads, *qAct_setContents, *qAct_inline;
+	QAction* qAct_settings;
 	QAction* qAct_about, *qAct_aboutQt;
 	
 	ForgeWindow ();
 	void buildObjList ();
 	void setTitle ();
+	void refresh ();
 	
 	// Where would a new item be inserted into?
 	ulong getInsertionPoint ();
@@ -75,6 +77,8 @@
 	void slot_copy ();
 	void slot_paste ();
 	
+	void slot_settings ();
+	
 	void slot_about ();
 	void slot_aboutQt ();
 };
Binary file icons/clips/folder.png has changed
Binary file icons/file-open.png has changed
Binary file icons/folder.png has changed
--- a/ldforge.pro	Mon Mar 18 13:16:21 2013 +0200
+++ b/ldforge.pro	Mon Mar 18 18:29:02 2013 +0200
@@ -19,7 +19,8 @@
 	str.h \
 	config.h \
 	cfgdef.h \
-	zz_setContentsDialog.h
+	zz_setContentsDialog.h \
+	zz_configDialog.h
 
 SOURCES += bbox.cpp \
 	gldraw.cpp \
@@ -30,7 +31,8 @@
 	misc.cpp \
 	str.cpp \ 
 	config.cpp \
-	zz_setContentsDialog.cpp
+	zz_setContentsDialog.cpp \
+	zz_configDialog.cpp
 
 QMAKE_CXXFLAGS += -std=c++0x
 QT += opengl
\ No newline at end of file
--- a/ldtypes.cpp	Mon Mar 18 13:16:21 2013 +0200
+++ b/ldtypes.cpp	Mon Mar 18 18:29:02 2013 +0200
@@ -3,7 +3,7 @@
 #include "file.h"
 #include "misc.h"
 
-const char* g_saObjTypeNames[] = {
+char const* g_saObjTypeNames[] = {
 	"unidentified",
 	"unknown",
 	"empty",
@@ -152,7 +152,7 @@
 }
 
 str LDVertex::getContents () {
-	return "!LDFORGE VERTEX"; // TODO
+	return str::mkfmt ("0 !LDFORGE VERTEX %d %s", dColor, vPosition.getStringRep (false).chars());
 }
 
 str LDEmpty::getContents () {
@@ -203,6 +203,17 @@
 	delete this;
 }
 
+void LDObject::replace (LDObject* replacement) {
+	// Replace all instances of the old object with the new object
+	for (ulong i = 0; i < g_CurrentFile->objects.size(); ++i) {
+		if (g_CurrentFile->objects[i] == this)
+			g_CurrentFile->objects[i] = replacement;
+	}
+	
+	// Remove the old object
+	delete this;
+}
+
 LDObject::~LDObject () {}
 LDComment::~LDComment () {}
 LDCondLine::~LDCondLine () {}
--- a/ldtypes.h	Mon Mar 18 13:16:21 2013 +0200
+++ b/ldtypes.h	Mon Mar 18 18:29:02 2013 +0200
@@ -60,6 +60,10 @@
 	
 	void commonInit ();
 	
+	// Replace this LDObject with another LDObject. This method deletes the
+	// object and any pointers to it become invalid.
+    void replace (LDObject* replacement);
+	
 	QTreeWidgetItem* qObjListEntry;
 };
 
@@ -223,6 +227,7 @@
 public:
 	IMPLEMENT_LDTYPE (Vertex)
 	
+	short dColor;
 	vertex vPosition;
 };
 
--- a/main.cpp	Mon Mar 18 13:16:21 2013 +0200
+++ b/main.cpp	Mon Mar 18 18:29:02 2013 +0200
@@ -3,6 +3,7 @@
 #include "file.h"
 #include "bbox.h"
 #include "misc.h"
+#include "config.h"
 
 vector<OpenFile*> g_LoadedFiles;
 OpenFile* g_CurrentFile = NULL;
@@ -10,11 +11,14 @@
 bbox g_BBox;
 
 int main (int dArgC, char* saArgV[]) {
-	// TODO
-	g_zaFileLoadPaths.push_back (".");
-	g_zaFileLoadPaths.push_back ("/home/arezey/ldraw/parts");
-	g_zaFileLoadPaths.push_back ("/home/arezey/ldraw/parts/s");
-	g_zaFileLoadPaths.push_back ("/home/arezey/ldraw/p");
+	// Load or create the configuration
+	if (!config::load()) {
+		printf ("Creating configuration file...\n");
+		if (config::save ())
+			printf ("Configuration file successfully created.\n");
+		else
+			printf ("failed to create configuration file!\n");
+	}
 	
 	QApplication app (dArgC, saArgV);
 	ForgeWindow* win = new ForgeWindow;
--- a/misc.cpp	Mon Mar 18 13:16:21 2013 +0200
+++ b/misc.cpp	Mon Mar 18 18:29:02 2013 +0200
@@ -9,6 +9,9 @@
 	return atol ((s / " ")[n]);
 }
 
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
 vertex parseVertex (str& s, const ushort n) {
 	vertex v;
 	v.x = getWordFloat (s, n);
@@ -18,6 +21,9 @@
 	return v;
 }
 
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
 void stripWhitespace (str& s) {
 	str other;
 	
@@ -26,6 +32,9 @@
 			other += s[i];
 }
 
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
 vertex bearing::project (vertex& vSource, ulong ulLength) {
 	vertex vDest = vSource;
 	
@@ -37,9 +46,7 @@
 }
 
 // =============================================================================
-// str ftoa (double)
-//
-// Converts a double-precision float to a string value.
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 str ftoa (double fCoord) {
 	str zRep = str::mkfmt ("%.3f", fCoord);
@@ -57,10 +64,7 @@
 }
 
 // =============================================================================
-// isNumber (str&)
-//
-// Returns whether a given string represents a floating point number
-// TODO: Does LDraw support scientific notation?
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 bool isNumber (str& zToken) {
 	char* cpPointer = &zToken[0];
--- a/misc.h	Mon Mar 18 13:16:21 2013 +0200
+++ b/misc.h	Mon Mar 18 18:29:02 2013 +0200
@@ -12,9 +12,12 @@
 long getWordInt (str& s, const ushort n);
 vertex parseVertex (str& s, const ushort n);
 void stripWhitespace (str& s);
+
+// Returns whether a given string represents a floating point number
+// TODO: Does LDraw support scientific notation?
 bool isNumber (str& zToken);
 
-// Float to string
+// Converts a float value to a string value.
 str ftoa (double fCoord);
 
 #endif // __MISC_H__
\ No newline at end of file
--- a/str.cpp	Mon Mar 18 13:16:21 2013 +0200
+++ b/str.cpp	Mon Mar 18 18:29:02 2013 +0200
@@ -33,15 +33,22 @@
 
 str::str (const char* c) {
 	text = new char[1];
-	clear ();
-	alloclen = strlen (text);
+	text[0] = '\0';
+	curs = alloclen = 0;
 	append (c);
 }
 
 str::str (char c) {
 	text = new char[1];
-	clear ();
-	alloclen = strlen (text);
+	text[0] = '\0';
+	curs = alloclen = 0;
+	append (c);
+}
+
+str::str (QString c) {
+	text = new char[1];
+	text[0] = '\0';
+	curs = alloclen = 0;
 	append (c);
 }
 
--- a/str.h	Mon Mar 18 13:16:21 2013 +0200
+++ b/str.h	Mon Mar 18 18:29:02 2013 +0200
@@ -33,7 +33,7 @@
 	str ();
 	str (const char* c);
 	str (char c);
-	str (const QString c);
+	str (QString c);
 	~str ();
 	
 	static str mkfmt (const char* fmt, ...) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zz_configDialog.cpp	Mon Mar 18 18:29:02 2013 +0200
@@ -0,0 +1,69 @@
+#include "common.h"
+#include "zz_configDialog.h"
+#include "file.h"
+#include <qgridlayout.h>
+#include <qfiledialog.h>
+
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
+ConfigDialog::ConfigDialog (ForgeWindow* parent) : QDialog () {
+	qLDrawPath = new QLineEdit;
+	qLDrawPath->setText (io_ldpath.value.chars());
+	
+	qLDrawPathLabel = new QLabel ("LDraw path:");
+	
+	qLDrawPathFindButton = new QPushButton;
+	qLDrawPathFindButton->setIcon (QIcon ("icons/folder.png"));
+	connect (qLDrawPathFindButton, SIGNAL (clicked ()),
+		this, SLOT (slot_findLDrawPath ()));
+	
+	qButtons = new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+	connect (qButtons, SIGNAL (accepted ()), this, SLOT (accept ()));
+	connect (qButtons, SIGNAL (rejected ()), this, SLOT (reject ()));
+	
+	QGridLayout* layout = new QGridLayout;
+	layout->addWidget (qLDrawPathLabel, 0, 0);
+	layout->addWidget (qLDrawPath, 0, 1);
+	layout->addWidget (qLDrawPathFindButton, 0, 2);
+	layout->addWidget (qButtons, 2, 1, 1, 2);
+	setLayout (layout);
+	
+	setWindowTitle (APPNAME_DISPLAY " - editing settings");
+}
+
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
+void ConfigDialog::slot_findLDrawPath () {
+	str zDir = QFileDialog::getExistingDirectory (this, "Choose LDraw directory",
+		qLDrawPath->text());
+	
+	if (~zDir)
+		qLDrawPath->setText (zDir.chars());
+}
+
+// =============================================================================
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+// =============================================================================
+void ConfigDialog::staticDialog (ForgeWindow* window) {
+	ConfigDialog dlg (window);
+	ulong ulChange = 0;
+	
+	if (dlg.exec ()) {
+		str zOldLDPath = io_ldpath;
+		io_ldpath = dlg.qLDrawPath->text();
+		
+		if (io_ldpath != zOldLDPath) {
+			ulChange |= (1 << 1);
+		}
+		
+		if (ulChange != 0)
+			config::save ();
+		
+		if (ulChange & (1 << 1)) {
+			// Reload all subfiles
+			reloadAllSubfiles ();
+		}
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/zz_configDialog.h	Mon Mar 18 18:29:02 2013 +0200
@@ -0,0 +1,23 @@
+#include "gui.h"
+#include <QDialog>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qdialogbuttonbox.h>
+#include <qpushbutton.h>
+
+class ConfigDialog : public QDialog {
+	Q_OBJECT
+	
+public:
+	QLabel* qLDrawPathLabel;
+	QLineEdit* qLDrawPath;
+	QPushButton* qLDrawPathFindButton;
+	
+	QDialogButtonBox* qButtons;
+	
+	ConfigDialog (ForgeWindow* parent);
+	static void staticDialog (ForgeWindow* window);
+	
+private slots:
+	void slot_findLDrawPath ();
+};
\ No newline at end of file
--- a/zz_setContentsDialog.cpp	Mon Mar 18 13:16:21 2013 +0200
+++ b/zz_setContentsDialog.cpp	Mon Mar 18 18:29:02 2013 +0200
@@ -66,14 +66,7 @@
 		// Reinterpret it from the text of the input field
 		obj = parseLine (dlg.qContents->text ().toStdString ().c_str ());
 		
-		// Remove the old object
-		delete oldobj;
-		
-		// Replace all instances of the old object with the new object
-		for (ulong i = 0; i < g_CurrentFile->objects.size(); ++i) {
-			if (g_CurrentFile->objects[i] == oldobj)
-				g_CurrentFile->objects[i] = obj;
-		}
+		oldobj->replace (obj);
 		
 		// Rebuild stuff after this
 		parent->buildObjList ();

mercurial