Multiple file support works now! woo!

Tue, 16 Jul 2013 01:33:11 +0300

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Tue, 16 Jul 2013 01:33:11 +0300
changeset 388
7ff483614aa1
parent 387
248296088f2c
child 389
ccb763dbe98e

Multiple file support works now! woo!

changelog.txt file | annotate | diff | comparison | revisions
src/addObjectDialog.cpp file | annotate | diff | comparison | revisions
src/extprogs.cpp file | annotate | diff | comparison | revisions
src/file.cpp file | annotate | diff | comparison | revisions
src/file.h file | annotate | diff | comparison | revisions
src/gui.cpp file | annotate | diff | comparison | revisions
src/gui.h file | annotate | diff | comparison | revisions
src/gui_actions.cpp file | annotate | diff | comparison | revisions
src/gui_editactions.cpp file | annotate | diff | comparison | revisions
src/history.cpp file | annotate | diff | comparison | revisions
src/ldtypes.cpp file | annotate | diff | comparison | revisions
src/ldtypes.h file | annotate | diff | comparison | revisions
src/main.cpp file | annotate | diff | comparison | revisions
src/types.cpp file | annotate | diff | comparison | revisions
--- a/changelog.txt	Mon Jul 15 22:32:23 2013 +0300
+++ b/changelog.txt	Tue Jul 16 01:33:11 2013 +0300
@@ -1,6 +1,8 @@
 =================================================
 == Changes in version 0.2.999-internal
 =================================================
+- Multiple files can now be kept open at the same time. A new list widget is on the left to contain the list
+	of currently open files.
 - Color icon border now reflects the color's edge color.
 
 =================================================
--- a/src/addObjectDialog.cpp	Mon Jul 15 22:32:23 2013 +0300
+++ b/src/addObjectDialog.cpp	Tue Jul 16 01:33:11 2013 +0300
@@ -398,7 +398,7 @@
 	
 	if (newObject) {
 		ulong idx = g_win->getInsertionPoint ();
-		currentFile()->insertObj (idx, obj);
+		LDOpenFile::current()->insertObj (idx, obj);
 	}
 	
 	g_win->fullRefresh ();
--- a/src/extprogs.cpp	Mon Jul 15 22:32:23 2013 +0300
+++ b/src/extprogs.cpp	Tue Jul 16 01:33:11 2013 +0300
@@ -166,7 +166,7 @@
 void writeColorGroup (const short colnum, str fname) {
 	vector<LDObject*> objects;
 	
-	for (LDObject* obj : *currentFile()) {
+	for (LDObject* obj : *LDOpenFile::current()) {
 		if (obj->isColored() == false || obj->color() != colnum)
 			continue;
 		
@@ -263,7 +263,7 @@
 			continue;
 		}
 		
-		currentFile()->addObject (obj);
+		LDOpenFile::current()->addObject (obj);
 		g_win->sel() << obj;
 	}
 	
--- a/src/file.cpp	Mon Jul 15 22:32:23 2013 +0300
+++ b/src/file.cpp	Tue Jul 16 01:33:11 2013 +0300
@@ -38,6 +38,8 @@
 static const int g_MaxRecentFiles = 5;
 static bool g_aborted = false;
 
+LDOpenFile* LDOpenFile::m_curfile = null;
+
 DEFINE_PROPERTY (QListWidgetItem*, LDOpenFile, listItem, setListItem)
 
 // =============================================================================
@@ -155,10 +157,10 @@
 	relpath.replace ("\\", "/");
 #endif // WIN32
 	
-	if (currentFile()) {
+	if (LDOpenFile::current()) {
 		// First, try find the file in the current model's file path. We want a file
 		// in the immediate vicinity of the current model to override stock LDraw stuff.
-		str partpath = fmt ("%1" DIRSLASH "%2", dirname (currentFile()->name ()), relpath);
+		str partpath = fmt ("%1" DIRSLASH "%2", dirname (LDOpenFile::current()->name ()), relpath);
 		
 		if (f->open (partpath, File::Read)) {
 			return f;
@@ -335,27 +337,15 @@
 	if (!f)
 		return null;
 	
-	LDOpenFile* oldLoad = currentFile();
 	LDOpenFile* load = new LDOpenFile;
 	load->setName (path);
 	
-	if (g_loadingMainFile) {
-		setCurrentFile (load);
-		g_win->R()->setFile (load);
-	}
-	
 	ulong numWarnings;
 	bool ok;
 	vector<LDObject*> objs = loadFileContents (f, &numWarnings, &ok);
 	
-	if (!ok) {
-		if (g_loadingMainFile) {
-			setCurrentFile (oldLoad);
-			g_win->R()->setFile (oldLoad);
-		}
-		
+	if (!ok)
 		return null;
-	}
 	
 	for (LDObject* obj : objs)
 		load->addObject (obj);
@@ -363,8 +353,11 @@
 	delete f;
 	g_loadedFiles << load;
 	
-	if (g_loadingMainFile)
-		log (QObject::tr ("File %1 parsed successfully (%2 warnings)."), path, numWarnings);
+	if (g_loadingMainFile) {
+		LDOpenFile::setCurrent (load);
+		g_win->R()->setFile (load);
+		log (QObject::tr ("File %1 parsed successfully (%2 errors)."), path, numWarnings);
+	}
 	
 	return load;
 }
@@ -389,7 +382,7 @@
 			// If we don't have a file path yet, we have to ask the user for one.
 			if (name().length() == 0) {
 				str newpath = QFileDialog::getSaveFileName (g_win, "Save As",
-					currentFile()->name(), "LDraw files (*.dat *.ldr)");
+					LDOpenFile::current()->name(), "LDraw files (*.dat *.ldr)");
 				
 				if (newpath.length() == 0)
 					return false;
@@ -433,7 +426,7 @@
 	
 	// Clear the array
 	g_loadedFiles.clear();
-	setCurrentFile (null);
+	LDOpenFile::setCurrent (null);
 	
 	g_win->R()->setFile (null);
 	g_win->fullRefresh();
@@ -450,7 +443,7 @@
 	f->setName ("");
 	f->setImplicit (false);
 	g_loadedFiles << f;
-	setCurrentFile (f);
+	LDOpenFile::setCurrent (f);
 	
 	g_win->R()->setFile (f);
 	g_win->fullRefresh();
@@ -494,8 +487,6 @@
 // =============================================================================
 void openMainFile (str path) {
 	g_loadingMainFile = true;
-	closeAll();
-	
 	LDOpenFile* file = openDATFile (path, false);
 	
 	if (!file) {
@@ -514,7 +505,6 @@
 	}
 	
 	file->setImplicit (false);
-	setCurrentFile (file);
 	
 	// Rebuild the object tree view now.
 	g_win->fullRefresh();
@@ -525,6 +515,8 @@
 	// Add it to the recent files list.
 	addRecentFile (path);
 	g_loadingMainFile = false;
+	
+	LDOpenFile::setCurrent (file);
 }
 
 // =============================================================================
@@ -587,8 +579,7 @@
 #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 LDErrorObject (line, fmt ("Token #%1 was `%2`, expected a number", (i + 1), tokens[i]));
 
 static vertex parseVertex (QStringList& s, const ushort n) {
 	vertex v;
@@ -631,7 +622,7 @@
 		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);
+					return new LDBFCObject ((LDBFCObject::Type) i);
 			
 			// MLCAD is notorious for stuffing these statements in parts it
 			// creates. The above block only handles valid statements, so we
@@ -739,7 +730,13 @@
 		CHECK_TOKEN_NUMBERS (1, 13)
 		
 		// Quadrilateral / Conditional line
-		LDObject* obj = (num == 4) ? ( (LDObject*) new LDQuadObject) : ( (LDObject*) new LDCondLineObject);
+		LDObject* obj;
+		
+		if (num == 4)
+			obj = new LDQuadObject;
+		else
+			obj = new LDCondLineObject;
+		
 		obj->setColor (tokens[1].toLong());
 		
 		for (short i = 0; i < 4; ++i)
@@ -771,14 +768,14 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 void reloadAllSubfiles () {
-	if (!currentFile())
+	if (!LDOpenFile::current())
 		return;
 	
 	g_loadedFiles.clear();
-	g_loadedFiles << currentFile();
+	g_loadedFiles << LDOpenFile::current();
 	
 	// Go through all objects in the current file and reload the subfiles
-	for (LDObject* obj : currentFile()->objs()) {
+	for (LDObject* obj : LDOpenFile::current()->objs()) {
 		if (obj->getType() == LDObject::Subfile) {
 			LDSubfileObject* ref = static_cast<LDSubfileObject*> (obj);
 			LDOpenFile* fileInfo = getFile (ref->fileInfo()->name());
@@ -803,12 +800,13 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 ulong LDOpenFile::addObject (LDObject* obj) {
-	PROP_NAME (history).add (new AddHistory (PROP_NAME (objs).size(), obj));
-	PROP_NAME (objs) << obj;
+	m_history.add (new AddHistory (m_objs.size(), obj));
+	m_objs << obj;
 	
 	if (obj->getType() == LDObject::Vertex)
-		PROP_NAME (vertices) << obj;
+		m_vertices << obj;
 	
+	obj->setFile (this);
 	return numObjs() - 1;
 }
 
@@ -818,15 +816,17 @@
 void LDOpenFile::insertObj (const ulong pos, LDObject* obj) {
 	m_history.add (new AddHistory (pos, obj));
 	m_objs.insert (pos, obj);
+	obj->setFile (this);
 }
 
 // =============================================================================
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 void LDOpenFile::forgetObject (LDObject* obj) {
-	ulong idx = obj->getIndex (this);
+	ulong idx = obj->getIndex();
 	m_history.add (new DelHistory (idx, obj));
 	m_objs.erase (idx);
+	obj->setFile (null);
 }
 
 // =============================================================================
@@ -847,6 +847,7 @@
 	str newcode = obj->raw();
 	m_history << new EditHistory (idx, oldcode, newcode);
 	
+	obj->setFile (this);
 	m_objs[idx] = obj;
 }
 
@@ -868,7 +869,7 @@
 // =============================================================================
 // Find out which files are unused and close them.
 void LDOpenFile::closeUnused () {
-	vector<LDOpenFile*> filesUsed = getFilesUsed (currentFile());
+	vector<LDOpenFile*> filesUsed = getFilesUsed (LDOpenFile::current());
 	
 	// Anything that's explicitly opened must not be closed
 	for (LDOpenFile* file : g_loadedFiles)
@@ -914,7 +915,7 @@
 
 LDOpenFile& LDOpenFile::operator<< (vector<LDObject*> objs) {
 	for (LDObject* obj : objs)
-		m_objs << obj;
+		addObject (obj);
 	
 	return *this;
 }
@@ -924,27 +925,13 @@
 }
 
 // =============================================================================
-class {
-public:
-	LDOpenFile* currentFile() {
-		return m_curfile;
-	}
-	
-	void setCurrentFile (LDOpenFile* f) {
-		m_curfile = f;
-		
-		if (g_win)
-			g_win->updateFileList();
-	}
-	
-private:
-	LDOpenFile* m_curfile;
-} g_currentFileShell;
-
-LDOpenFile* currentFile() {
-	return g_currentFileShell.currentFile();
+LDOpenFile* LDOpenFile::current() {
+	return m_curfile;
 }
 
-void setCurrentFile (LDOpenFile* f) {
-	g_currentFileShell.setCurrentFile (f);
+void LDOpenFile::setCurrent (LDOpenFile* f) {
+	m_curfile = f;
+	
+	if (g_win && f)
+		g_win->updateFileListItem (f);
 }
\ No newline at end of file
--- a/src/file.h	Mon Jul 15 22:32:23 2013 +0300
+++ b/src/file.h	Tue Jul 16 01:33:11 2013 +0300
@@ -24,6 +24,8 @@
 #include "history.h"
 #include <QObject>
 
+#define curfile LDOpenFile::current()
+
 class History;
 class OpenProgressDialog;
 
@@ -98,8 +100,6 @@
 		return PROP_NAME (objs).end();
 	}
 	
-	static void closeUnused();
-	
 	void openHistory() {
 		m_history.open();
 	}
@@ -123,6 +123,13 @@
 	void addToHistory (AbstractHistoryEntry* entry) {
 		m_history << entry;
 	}
+	
+	static void closeUnused();
+	static LDOpenFile* current();
+	static void setCurrent (LDOpenFile* f);
+	
+private:
+	static LDOpenFile* m_curfile;
 };
 
 // Close all current loaded files and start off blank.
@@ -163,8 +170,6 @@
 void addRecentFile (str path);
 str basename (str path);
 str dirname (str path);
-LDOpenFile* currentFile();
-void setCurrentFile (LDOpenFile* f);
 
 extern vector<LDOpenFile*> g_loadedFiles; // Vector of all currently opened files.
 
@@ -201,6 +206,4 @@
 	void workDone();
 };
 
-void changeCurrentFile (LDOpenFile* f);
-
 #endif // FILE_H
\ No newline at end of file
--- a/src/gui.cpp	Mon Jul 15 22:32:23 2013 +0300
+++ b/src/gui.cpp	Tue Jul 16 01:33:11 2013 +0300
@@ -85,6 +85,7 @@
 	
 	connect (ui->objectList, SIGNAL (itemSelectionChanged()), this, SLOT (slot_selectionChanged()));
 	connect (ui->objectList, SIGNAL (itemDoubleClicked (QListWidgetItem*)), this, SLOT (slot_editObject (QListWidgetItem*)));
+	connect (ui->fileList, SIGNAL (currentItemChanged (QListWidgetItem*, QListWidgetItem*)), this, SLOT (changeCurrentFile()));
 	
 	// Init message log manager
 	m_msglog = new MessageManager;
@@ -230,21 +231,21 @@
 	str title = fmt (APPNAME " %1", fullVersionString());
 	
 	// Append our current file if we have one
-	if (currentFile()) {
-		if (currentFile()->name().length() > 0)
-			title += fmt (": %1", basename (currentFile()->name()));
+	if (LDOpenFile::current()) {
+		if (LDOpenFile::current()->name().length() > 0)
+			title += fmt (": %1", basename (LDOpenFile::current()->name()));
 		else
 			title += fmt (": <anonymous>");
 		
-		if (currentFile()->numObjs() > 0 &&
-			currentFile()->obj (0)->getType() == LDObject::Comment)
+		if (LDOpenFile::current()->numObjs() > 0 &&
+			LDOpenFile::current()->obj (0)->getType() == LDObject::Comment)
 		{
 			// Append title
-			LDCommentObject* comm = static_cast<LDCommentObject*> (currentFile()->obj (0));
+			LDCommentObject* comm = static_cast<LDCommentObject*> (LDOpenFile::current()->obj (0));
 			title += fmt (": %1", comm->text);
 		}
 		
-		if (currentFile()->history().pos() != currentFile()->savePos())
+		if (LDOpenFile::current()->history().pos() != LDOpenFile::current()->savePos())
 			title += '*';
 	}
 	
@@ -264,7 +265,7 @@
 	
 	// Delete the objects that were being selected
 	for (LDObject* obj : selCopy) {
-		currentFile()->forgetObject (obj);
+		LDOpenFile::current()->forgetObject (obj);
 		++num;
 		delete obj;
 	}
@@ -277,7 +278,7 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 void ForgeWindow::buildObjList() {
-	if (!currentFile())
+	if (!LDOpenFile::current())
 		return;
 	
 	// Lock the selection while we do this so that refreshing the object list
@@ -290,7 +291,7 @@
 	
 	ui->objectList->clear();
 	
-	for (LDObject* obj : currentFile()->objs()) {
+	for (LDObject* obj : LDOpenFile::current()->objs()) {
 		str descr;
 		
 		switch (obj->getType()) {
@@ -403,7 +404,7 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 void ForgeWindow::slot_selectionChanged() {
-	if (g_bSelectionLocked == true || currentFile() == null)
+	if (g_bSelectionLocked == true || LDOpenFile::current() == null)
 		return;
 	
 	// Update the shared selection array, though don't do this if this was
@@ -418,7 +419,7 @@
 	m_sel.clear();
 	const QList<QListWidgetItem*> items = ui->objectList->selectedItems();
 	
-	for (LDObject* obj : currentFile()->objs())
+	for (LDObject* obj : LDOpenFile::current()->objs())
 	for (QListWidgetItem* item : items) {
 		if (item == obj->qObjListEntry) {
 			m_sel << obj;
@@ -450,7 +451,7 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 void ForgeWindow::slot_quickColor() {
-	currentFile()->openHistory();
+	LDOpenFile::current()->openHistory();
 	QToolButton* button = static_cast<QToolButton*> (sender());
 	LDColor* col = null;
 	
@@ -474,7 +475,7 @@
 	}
 	
 	fullRefresh();
-	currentFile()->closeHistory();
+	LDOpenFile::current()->closeHistory();
 }
 
 // =============================================================================
@@ -483,11 +484,11 @@
 ulong ForgeWindow::getInsertionPoint() {
 	if (m_sel.size() > 0) {
 		// If we have a selection, put the item after it.
-		return (m_sel[m_sel.size() - 1]->getIndex (currentFile())) + 1;
+		return (m_sel[m_sel.size() - 1]->getIndex()) + 1;
 	}
 	
 	// Otherwise place the object at the end.
-	return currentFile()->numObjs();
+	return LDOpenFile::current()->numObjs();
 }
 
 // =============================================================================
@@ -509,7 +510,7 @@
 void ForgeWindow::updateSelection() {
 	g_bSelectionLocked = true;
 	
-	for (LDObject* obj : currentFile()->objs())
+	for (LDObject* obj : LDOpenFile::current()->objs())
 		obj->setSelected (false);
 	
 	ui->objectList->clearSelection();
@@ -630,7 +631,7 @@
 // =============================================================================
 void ForgeWindow::deleteObjVector (vector<LDObject*> objs) {
 	for (LDObject* obj : objs) {
-		currentFile()->forgetObject (obj);
+		LDOpenFile::current()->forgetObject (obj);
 		delete obj;
 	}
 }
@@ -638,7 +639,7 @@
 // =============================================================================
 void ForgeWindow::deleteByColor (const short colnum) {
 	vector<LDObject*> objs;
-	for (LDObject* obj : currentFile()->objs()) {
+	for (LDObject* obj : LDOpenFile::current()->objs()) {
 		if (!obj->isColored() || obj->color() != colnum)
 			continue;
 		
@@ -657,7 +658,7 @@
 
 void ForgeWindow::slot_editObject (QListWidgetItem* listitem) {
 	LDObject* obj = null;
-	for (LDObject* it : *currentFile()) {
+	for (LDObject* it : *LDOpenFile::current()) {
 		if (it->qObjListEntry == listitem) {
 			obj = it;
 			break;
@@ -695,7 +696,7 @@
 	
 	if (path.length() == 0 || saveAs) {
 		path = QFileDialog::getSaveFileName (g_win, tr ("Save As"),
-			currentFile()->name(), tr ("LDraw files (*.dat *.ldr)"));
+			LDOpenFile::current()->name(), tr ("LDraw files (*.dat *.ldr)"));
 		
 		if (path.length() == 0) {
 			// User didn't give a file name. This happens if the user cancelled
@@ -707,7 +708,7 @@
 	if (f->save (path)) {
 		f->setName (path);
 		
-		if (f == currentFile())
+		if (f == LDOpenFile::current())
 			g_win->updateTitle();
 		
 		log ("Saved to %1.", path);
@@ -788,7 +789,7 @@
 void makeColorSelector (QComboBox* box) {
 	std::map<short, ulong> counts;
 	
-	for (LDObject* obj : currentFile()->objs()) {
+	for (LDObject* obj : LDOpenFile::current()->objs()) {
 		if (!obj->isColored())
 			continue;
 		
@@ -852,13 +853,21 @@
 }
 
 void ForgeWindow::updateFileListItem (LDOpenFile* f) {
+	if (f->listItem() == null) {
+		// We don't have a list item for this file, so the list
+		// doesn't exist yet. Create it - afterwards this will be
+		// up to date.
+		updateFileList();
+		return;
+	}
+	
 	str name;
 	if (f->name() == "")
 		name = "<anonymous>";
 	else
 		name = basename (f->name());
 	
-	if (f == currentFile())
+	if (f == LDOpenFile::current())
 		ui->fileList->setCurrentItem (f->listItem());
 	
 	f->listItem()->setText (name);
@@ -868,13 +877,49 @@
 void ForgeWindow::beginAction (QAction* act) {
 	// Open the history so we can record the edits done during this action.
 	if (act != ACTION (Undo) && act != ACTION (Redo) && act != ACTION (Open))
-		currentFile()->openHistory();
+		LDOpenFile::current()->openHistory();
 }
 
 void ForgeWindow::endAction() {
 	// Close the history now.
-	currentFile()->closeHistory();
-	updateFileListItem (currentFile());
+	LDOpenFile::current()->closeHistory();
+	updateFileListItem (LDOpenFile::current());
+}
+
+void ForgeWindow::changeCurrentFile() {
+	LDOpenFile* f = null;
+	QListWidgetItem* item = ui->fileList->currentItem();
+	
+	for (LDOpenFile* it : g_loadedFiles) {
+		if (it->listItem() == item) {
+			f = it;
+			break;
+		}
+	}
+	
+	if (!f)
+		return;
+	
+	clearSelection();
+	LDOpenFile::setCurrent (f);
+	
+	log ("Changed file to %1", basename (f->name()));
+	
+	R()->setFile (f);
+	R()->update();
+	buildObjList();
+}
+
+void ForgeWindow::refreshObjectList() {
+#if 0
+	ui->objectList->clear();
+	LDOpenFile* f = LDOpenFile::current();
+	
+	for (LDObject* obj : *f)
+		ui->objectList->addItem (obj->qObjListEntry);
+#endif
+	
+	buildObjList();
 }
 
 QImage imageFromScreencap (uchar* data, ushort w, ushort h) {
--- a/src/gui.h	Mon Jul 15 22:32:23 2013 +0300
+++ b/src/gui.h	Tue Jul 16 01:33:11 2013 +0300
@@ -118,6 +118,7 @@
 	void setStatusBarText (str text);
 	void addMessage (str msg);
 	Ui_LDForgeUI* interface() const;
+	void refreshObjectList();
 	
 	void beginAction(QAction* act);
 	void endAction();
@@ -131,6 +132,7 @@
 	void primitiveLoaderEnd();
 	void clearSelection();
 	void slot_action();
+	void changeCurrentFile();
 	
 protected:
 	void closeEvent (QCloseEvent* ev);
--- a/src/gui_actions.cpp	Mon Jul 15 22:32:23 2013 +0300
+++ b/src/gui_actions.cpp	Tue Jul 16 01:33:11 2013 +0300
@@ -66,17 +66,18 @@
 		ui.rb_license_nonca->isChecked() ? NonCALicense :
 		                                   "";
 	
-	*currentFile() << new LDCommentObject (ui.le_title->text());
-	*currentFile() << new LDCommentObject ("Name: <untitled>.dat" );
-	*currentFile() << new LDCommentObject (fmt ("Author: %1", ui.le_author->text()));
-	*currentFile() << new LDCommentObject (fmt ("!LDRAW_ORG Unofficial_Part"));
+	LDOpenFile* f = LDOpenFile::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 != "")
-		*currentFile() << new LDCommentObject (license);
+		*f << new LDCommentObject (license);
 	
-	*currentFile() << new LDEmptyObject;
-	*currentFile() << new LDBFCObject (BFCType);
-	*currentFile() << new LDEmptyObject;
+	*f << new LDEmptyObject;
+	*f << new LDBFCObject (BFCType);
+	*f << new LDEmptyObject;
 	
 	g_win->fullRefresh();
 }
@@ -85,15 +86,11 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 DEFINE_ACTION (Open, CTRL (O)) {
-	if (safeToCloseAll() == false)
-		return;
-	
 	str name = QFileDialog::getOpenFileName (g_win, "Open File", "", "LDraw files (*.dat *.ldr)");
 	
 	if (name.length() == 0)
 		return;
 	
-	closeAll();
 	openMainFile (name);
 }
 
@@ -101,14 +98,14 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 DEFINE_ACTION (Save, CTRL (S)) {
-	g_win->save (currentFile(), false);
+	g_win->save (LDOpenFile::current(), false);
 }
 
 // =============================================================================
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 DEFINE_ACTION (SaveAs, CTRL_SHIFT (S)) {
-	g_win->save (currentFile(), true);
+	g_win->save (LDOpenFile::current(), true);
 }
 
 // =============================================================================
@@ -198,7 +195,7 @@
 DEFINE_ACTION (SelectAll, CTRL (A)) {
 	g_win->sel().clear();
 	
-	for (LDObject* obj : currentFile()->objs())
+	for (LDObject* obj : LDOpenFile::current()->objs())
 		g_win->sel() << obj;
 	
 	g_win->updateSelection();
@@ -212,7 +209,7 @@
 		return; // no consensus on color
 	
 	g_win->sel().clear();
-	for (LDObject* obj : currentFile()->objs())
+	for (LDObject* obj : LDOpenFile::current()->objs())
 		if (obj->color() == colnum)
 			g_win->sel() << obj;
 	
@@ -242,7 +239,7 @@
 	}
 	
 	g_win->sel().clear();
-	for (LDObject* obj : currentFile()->objs()) {
+	for (LDObject* obj : LDOpenFile::current()->objs()) {
 		if (obj->getType() != type)
 			continue;
 		
@@ -302,7 +299,7 @@
 	g_win->sel().clear();
 	
 	for (LDObject* obj : objs) {
-		currentFile()->insertObj (idx, obj);
+		LDOpenFile::current()->insertObj (idx, obj);
 		g_win->sel() << obj;
 		
 		idx++;
@@ -363,7 +360,7 @@
 	for (str line : str (te_edit->toPlainText()).split ("\n")) {
 		LDObject* obj = parseLine (line);
 		
-		currentFile()->insertObj (idx, obj);
+		LDOpenFile::current()->insertObj (idx, obj);
 		g_win->sel() << obj;
 		idx++;
 	}
@@ -379,7 +376,7 @@
 	uchar* imgdata = g_win->R()->screencap (w, h);
 	QImage img = imageFromScreencap (imgdata, w, h);
 	
-	str root = basename (currentFile()->name());
+	str root = basename (LDOpenFile::current()->name());
 	if (root.right (4) == ".dat")
 		root.chop (4);
 	
--- a/src/gui_editactions.cpp	Mon Jul 15 22:32:23 2013 +0300
+++ b/src/gui_editactions.cpp	Tue Jul 16 01:33:11 2013 +0300
@@ -89,7 +89,7 @@
 	
 	for (str line : clipboardText.split ("\n")) {
 		LDObject* pasted = parseLine (line);
-		currentFile()->insertObj (idx++, pasted);
+		LDOpenFile::current()->insertObj (idx++, pasted);
 		g_win->sel() << pasted;
 		g_win->R()->compileObject (pasted);
 		++num;
@@ -117,7 +117,7 @@
 	for (LDObject* obj : sel) {
 		// Get the index of the subfile so we know where to insert the
 		// inlined contents.
-		long idx = obj->getIndex (currentFile());
+		long idx = obj->getIndex();
 		
 		if (idx == -1)
 			continue;
@@ -135,12 +135,12 @@
 			delete inlineobj;
 		
 			LDObject* newobj = parseLine (line);
-			currentFile()->insertObj (idx++, newobj);
+			LDOpenFile::current()->insertObj (idx++, newobj);
 			g_win->sel() << newobj;
 		}
 		
 		// Delete the subfile now as it's been inlined.
-		currentFile()->forgetObject (obj);
+		LDOpenFile::current()->forgetObject (obj);
 		delete obj;
 	}
 	
@@ -167,7 +167,7 @@
 			continue;
 		
 		// Find the index of this quad
-		long index = obj->getIndex (currentFile());
+		long index = obj->getIndex();
 		
 		if (index == -1)
 			return;
@@ -176,8 +176,8 @@
 		
 		// Replace the quad with the first triangle and add the second triangle
 		// after the first one.
-		currentFile()->setObject (index, triangles[0]);
-		currentFile()->insertObj (index + 1, triangles[1]);
+		LDOpenFile::current()->setObject (index, triangles[0]);
+		LDOpenFile::current()->insertObj (index + 1, triangles[1]);
 		
 		// Delete this quad now, it has been split.
 		delete obj;
@@ -286,10 +286,10 @@
 		}
 		
 		for (short i = 0; i < numLines; ++i) {
-			ulong idx = obj->getIndex (currentFile()) + i + 1;
+			ulong idx = obj->getIndex() + i + 1;
 			
 			lines[i]->setColor (edgecolor);
-			currentFile()->insertObj (idx, lines[i]);
+			LDOpenFile::current()->insertObj (idx, lines[i]);
 			g_win->R()->compileObject (lines[i]);
 		}
 		
@@ -310,14 +310,14 @@
 		if (obj->vertices() < 2)
 			continue;
 		
-		ulong idx = obj->getIndex (currentFile());
+		ulong idx = obj->getIndex();
 		
 		for (short i = 0; i < obj->vertices(); ++i) {
 			LDVertexObject* vert = new LDVertexObject;
 			vert->pos = obj->getVertex (i);
 			vert->setColor (obj->color());
 			
-			currentFile()->insertObj (++idx, vert);
+			LDOpenFile::current()->insertObj (++idx, vert);
 			g_win->R()->compileObject (vert);
 			++num;
 		}
@@ -348,11 +348,11 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 DEFINE_ACTION (Undo, CTRL (Z)) {
-	currentFile()->undo();
+	LDOpenFile::current()->undo();
 }
 
 DEFINE_ACTION (Redo, CTRL_SHIFT (Z)) {
-	currentFile()->redo();
+	LDOpenFile::current()->redo();
 }
 
 // =============================================================================
@@ -618,7 +618,7 @@
 
 // =================================================================================================
 static bool isColorUsed (short colnum) {
-	for (LDObject* obj : currentFile()->objs())
+	for (LDObject* obj : LDOpenFile::current()->objs())
 		if (obj->isColored() && obj->color() == colnum)
 			return true;
 	
--- a/src/history.cpp	Mon Jul 15 22:32:23 2013 +0300
+++ b/src/history.cpp	Tue Jul 16 01:33:11 2013 +0300
@@ -161,14 +161,14 @@
 
 // =============================================================================
 void EditHistory::undo() const {
-	LDObject* obj = currentFile()->object (index());
+	LDObject* obj = LDOpenFile::current()->object (index());
 	LDObject* newobj = parseLine (oldCode());
 	obj->replace (newobj);
 	g_win->R()->compileObject (newobj);
 }
 
 void EditHistory::redo() const {
-	LDObject* obj = currentFile()->object (index());
+	LDObject* obj = LDOpenFile::current()->object (index());
 	LDObject* newobj = parseLine (newCode());
 	obj->replace (newobj);
 	g_win->R()->compileObject (newobj);
--- a/src/ldtypes.cpp	Mon Jul 15 22:32:23 2013 +0300
+++ b/src/ldtypes.cpp	Tue Jul 16 01:33:11 2013 +0300
@@ -32,15 +32,16 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 // LDObject constructors
-LDObject::LDObject() {
-	qObjListEntry = null;
-	setParent (null);
-	m_hidden = false;
-	m_selected = false;
-	m_glinit = false;
-	
+LDObject::LDObject () :
+	m_hidden (false),
+	m_selected (false),
+	m_parent (null),
+	m_file (null),
+	qObjListEntry (null),
+	m_glinit (false)
+{
 	// Determine ID
-	int id = 1; // 0 is invalid
+	qint32 id = 1; // 0 is invalid
 	
 	for (LDObject* obj : g_LDObjects)
 		if (obj->id() >= id)
@@ -191,11 +192,11 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 void LDObject::replace (LDObject* other) {
-	long idx = getIndex (currentFile());
+	long idx = getIndex();
 	assert (idx != -1);
 	
 	// Replace the instance of the old object with the new object
-	currentFile()->setObject (idx, other);
+	LDOpenFile::current()->setObject (idx, other);
 	
 	// Remove the old object
 	delete this;
@@ -205,14 +206,14 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 void LDObject::swap (LDObject* other) {
-	for (LDObject*& obj : *currentFile()) {
+	for (LDObject*& obj : *LDOpenFile::current()) {
 		if (obj == this)
 			obj = other;
 		elif (obj == other)
 			obj = this;
 	}
 
-	currentFile()->addToHistory (new SwapHistory (id(), other->id()));
+	LDOpenFile::current()->addToHistory (new SwapHistory (id(), other->id()));
 }
 
 LDLineObject::LDLineObject (vertex v1, vertex v2) {
@@ -332,9 +333,13 @@
 // =============================================================================
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
-long LDObject::getIndex (LDOpenFile* file) const {
-	for (ulong i = 0; i < file->numObjs(); ++i)
-		if (file->obj (i) == this)
+long LDObject::getIndex() const {
+#ifndef RELEASE
+	assert (file() != null);
+#endif
+	
+	for (ulong i = 0; i < file()->numObjs(); ++i)
+		if (file()->obj (i) == this)
 			return i;
 	
 	return -1;
@@ -353,10 +358,10 @@
 	for (long i = start; i != end; i += incr) {
 		LDObject* obj = objs[i];
 		
-		const long idx = obj->getIndex (currentFile()),
+		const long idx = obj->getIndex(),
 			target = idx + (up ? -1 : 1);
 		
-		if ((up && idx == 0) || (!up && idx == (long) (currentFile()->objs().size() - 1))) {
+		if ((up && idx == 0) || (!up && idx == (long) (LDOpenFile::current()->objs().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.
@@ -365,9 +370,9 @@
 		}
 		
 		objsToCompile << obj;
-		objsToCompile << currentFile()->obj (target);
+		objsToCompile << LDOpenFile::current()->obj (target);
 		
-		obj->swap (currentFile()->obj (target));
+		obj->swap (LDOpenFile::current()->obj (target));
 	}
 	
 	objsToCompile.makeUnique();
@@ -437,24 +442,24 @@
 
 // =============================================================================
 LDObject* LDObject::next() const {
-	long idx = getIndex (currentFile());
+	long idx = getIndex();
 	assert (idx != -1);
 	
-	if (idx == (long) currentFile()->numObjs() - 1)
+	if (idx == (long) LDOpenFile::current()->numObjs() - 1)
 		return null;
 	
-	return currentFile()->obj (idx + 1);
+	return LDOpenFile::current()->obj (idx + 1);
 }
 
 // =============================================================================
 LDObject* LDObject::prev() const {
-	long idx = getIndex (currentFile());
+	long idx = getIndex();
 	assert (idx != -1);
 	
 	if (idx == 0)
 		return null;
 	
-	return currentFile()->obj (idx - 1);
+	return LDOpenFile::current()->obj (idx - 1);
 }
 
 // =============================================================================
@@ -552,14 +557,14 @@
 	// flipped but I don't have a method for checking flatness yet.
 	// Food for thought...
 	
-	ulong idx = getIndex (currentFile());
+	ulong idx = getIndex();
 	
 	if (idx > 0) {
 		LDBFCObject* bfc = dynamic_cast<LDBFCObject*> (prev());
 		
 		if (bfc && bfc->type == LDBFCObject::InvertNext) {
 			// This is prefixed with an invertnext, thus remove it.
-			currentFile()->forgetObject (bfc);
+			LDOpenFile::current()->forgetObject (bfc);
 			delete bfc;
 			return;
 		}
@@ -567,7 +572,7 @@
 	
 	// Not inverted, thus prefix it with a new invertnext.
 	LDBFCObject* bfc = new LDBFCObject (LDBFCObject::InvertNext);
-	currentFile()->insertObj (idx, bfc);
+	LDOpenFile::current()->insertObj (idx, bfc);
 }
 
 static void invertLine (LDObject* line) {
@@ -628,12 +633,12 @@
 template<class T> void changeProperty (LDObject* obj, T* ptr, const T& val) {
 	long idx;
 	
-	if ((idx = obj->getIndex (currentFile())) != -1) {
+	if (obj->file() && (idx = obj->getIndex()) != -1) {
 		str before = obj->raw();
 		*ptr = val;
 		str after = obj->raw();
 		
-		currentFile()->addToHistory (new EditHistory (idx, before, after));
+		LDOpenFile::current()->addToHistory (new EditHistory (idx, before, after));
 	} else
 		*ptr = val;
 }
--- a/src/ldtypes.h	Mon Jul 15 22:32:23 2013 +0300
+++ b/src/ldtypes.h	Tue Jul 16 01:33:11 2013 +0300
@@ -65,7 +65,8 @@
 	PROPERTY (bool, hidden, setHidden)
 	PROPERTY (bool, selected, setSelected)
 	PROPERTY (LDObject*, parent, setParent)
-	READ_PROPERTY (int, id, setID)
+	PROPERTY (LDOpenFile*, file, setFile)
+	READ_PROPERTY (qint32, id, setID)
 	DECLARE_PROPERTY (short, color, setColor)
 
 public:
@@ -89,18 +90,18 @@
 	LDObject();
 	virtual ~LDObject();
 	
-	virtual LDObject* clone() {return 0;}      // Creates a new LDObject identical to this one.
-	long getIndex (LDOpenFile* file) const;     // Index (i.e. line number) of this object
-	virtual LDObject::Type getType() const;    // Type enumerator of this object
+	virtual LDObject* clone() {return 0;}       // Creates a new LDObject identical to this one.
+	long getIndex () const;                     // Index (i.e. line number) of this object
+	virtual LDObject::Type getType() const;     // Type enumerator of this object
 	const vertex& getVertex (int i) const;      // Get a vertex by index
-	virtual bool hasMatrix() const;            // Does this object have a matrix and position? (see LDMatrixObject)
-	virtual void invert();                     // Inverts this object (winding is reversed)
-	virtual bool isColored() const;            // Is this object colored?
-	virtual bool isScemantic() const;          // Does this object have meaning in the part model?
+	virtual bool hasMatrix() const;             // Does this object have a matrix and position? (see LDMatrixObject)
+	virtual void invert();                      // Inverts this object (winding is reversed)
+	virtual bool isColored() const;             // Is this object colored?
+	virtual bool isScemantic() const;           // Does this object have meaning in the part model?
 	virtual void move (vertex vect);            // Moves this object using the given vertex as a movement vector
-	LDObject* next() const;                    // Object after this in the current file
-	LDObject* prev() const;                    // Object prior to this in the current file
-	virtual str raw() { return ""; }           // This object as LDraw code
+	LDObject* next() const;                     // Object after this in the current file
+	LDObject* prev() const;                     // Object prior to this in the current file
+	virtual str raw() { return ""; }            // This object as LDraw code
 	void replace (LDObject* other);             // Replace this LDObject with another LDObject. Object is deleted in the process.
 	void setVertex (int i, const vertex& vert); // Set a vertex to the given value
 	void setVertexCoord (int i, Axis ax, double value); // Set a single coordinate of a vertex
--- a/src/main.cpp	Mon Jul 15 22:32:23 2013 +0300
+++ b/src/main.cpp	Tue Jul 16 01:33:11 2013 +0300
@@ -60,7 +60,7 @@
 int main (int argc, char* argv[]) {
 	const QApplication app (argc, argv);
 	g_app = &app;
-	setCurrentFile (null);
+	LDOpenFile::setCurrent (null);
 	
 	// Load or create the configuration
 	if (!config::load ()) {
--- a/src/types.cpp	Mon Jul 15 22:32:23 2013 +0300
+++ b/src/types.cpp	Tue Jul 16 01:33:11 2013 +0300
@@ -498,10 +498,10 @@
 void LDBoundingBox::calculate() {
 	reset();
 	
-	if (!currentFile())
+	if (!LDOpenFile::current())
 		return;
 	
-	for (LDObject* obj : currentFile()->objs())
+	for (LDObject* obj : LDOpenFile::current()->objs())
 		calcObject (obj);
 }
 

mercurial