Commit work done on document manager. Happy 3rd birthday LDForge!

Tue, 22 Sep 2015 23:38:19 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Tue, 22 Sep 2015 23:38:19 +0300
changeset 997
1b49f34e533d
parent 995
7986584e7498
child 998
5be0ce31ce60

Commit work done on document manager. Happy 3rd birthday LDForge!

CMakeLists.txt file | annotate | diff | comparison | revisions
src/dialogs/configdialog.cpp file | annotate | diff | comparison | revisions
src/documentmanager.cpp file | annotate | diff | comparison | revisions
src/documentmanager.h file | annotate | diff | comparison | revisions
src/hierarchyelement.cpp file | annotate | diff | comparison | revisions
src/hierarchyelement.h file | annotate | diff | comparison | revisions
src/ldDocument.cpp file | annotate | diff | comparison | revisions
src/ldDocument.h file | annotate | diff | comparison | revisions
src/mainwindow.cpp file | annotate | diff | comparison | revisions
src/mainwindow.h file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Sun Sep 06 20:45:51 2015 +0300
+++ b/CMakeLists.txt	Tue Sep 22 23:38:19 2015 +0300
@@ -39,6 +39,7 @@
 	src/dialogs.cpp
 	src/documentation.cpp
 	src/documentloader.cpp
+	src/documentmanager.cpp
 	src/editHistory.cpp
 	src/glRenderer.cpp
 	src/glCompiler.cpp
@@ -87,6 +88,7 @@
 	src/dialogs.h
 	src/documentation.h
 	src/documentloader.h
+	src/documentmanager.h
 	src/doublemap.h
 	src/editHistory.h
 	src/format.h
--- a/src/dialogs/configdialog.cpp	Sun Sep 06 20:45:51 2015 +0300
+++ b/src/dialogs/configdialog.cpp	Tue Sep 22 23:38:19 2015 +0300
@@ -550,7 +550,7 @@
 
 	for (ShortcutListItem* item : sel)
 	{
-		item->setSequence (MainWindow::defaultShortcut (item->action()));
+		item->setSequence (m_window->defaultShortcut (item->action()));
 		setShortcutText (item);
 	}
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/documentmanager.cpp	Tue Sep 22 23:38:19 2015 +0300
@@ -0,0 +1,431 @@
+/*
+ *  LDForge: LDraw parts authoring CAD
+ *  Copyright (C) 2013 - 2015 Teemu 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 <QFileInfo>
+#include <QFile>
+#include "documentmanager.h"
+#include "ldDocument.h"
+#include "mainwindow.h"
+#include "partdownloader.h"
+
+enum
+{
+	MAX_RECENT_FILES = 10
+};
+
+DocumentManager::DocumentManager (QObject* parent) :
+	QObject (parent),
+	g_loadingMainFile (false),
+	g_loadingLogoedStuds (false),
+	g_logoedStud (nullptr),
+	g_logoedStud2 (nullptr) {}
+
+DocumentManager::~DocumentManager()
+{
+	clear();
+}
+
+void DocumentManager::clear()
+{
+	for (DocumentMapIterator it (m_documents); it.hasNext();)
+	{
+		LDDocument* document = it.next().value();
+		document->close();
+		delete document;
+	}
+
+	m_documents.clear();
+}
+
+LDDocument* DocumentManager::getDocumentByName (QString filename)
+{
+	// Try find the file in the list of loaded files
+	LDDocument* doc = findDocumentByName (filename);
+
+	// If it's not loaded, try open it
+	if (not doc)
+		doc = openDocument (filename, true, true);
+
+	return doc;
+}
+
+void DocumentManager::openMainModel (QString path)
+{
+	// If there's already a file with the same name, this file must replace it.
+	LDDocument* documentToReplace = nullptr;
+	LDDocument* file = nullptr;
+	QString shortName = LDDocument::shortenName (path);
+
+	for (LDDocument* doc : m_window->allDocuments())
+	{
+		if (doc->name() == shortName)
+		{
+			documentToReplace = doc;
+			break;
+		}
+	}
+
+	// We cannot open this file if the document this would replace is not
+	// safe to close.
+	if (documentToReplace and not documentToReplace->isSafeToClose())
+		return;
+
+	g_loadingMainFile = true;
+
+	// If we're replacing an existing document, clear the document and
+	// make it ready for being loaded to.
+	if (documentToReplace)
+	{
+		file = documentToReplace;
+		file->clear();
+	}
+
+	bool aborted;
+	file = openDocument (path, false, false, file, &aborted);
+
+	if (file == nullptr)
+	{
+		if (not aborted)
+		{
+			// Tell the user loading failed.
+			setlocale (LC_ALL, "C");
+			Critical (format (tr ("Failed to open %1: %2"), path, strerror (errno)));
+		}
+
+		g_loadingMainFile = false;
+		return;
+	}
+
+	file->openForEditing();
+	m_window->closeInitialDocument();
+	m_window->changeDocument (file);
+	m_window->doFullRefresh();
+	addRecentFile (path);
+	g_loadingMainFile = false;
+
+	// If there were problems loading subfile references, try see if we can find these
+	// files on the parts tracker.
+	QStringList unknowns;
+
+	for (LDObject* obj : file->objects())
+	{
+		if (obj->type() != OBJ_Error or static_cast<LDError*> (obj)->fileReferenced().isEmpty())
+			continue;
+
+		unknowns << static_cast<LDError*> (obj)->fileReferenced();
+	}
+
+	if (m_window->configBag()->tryDownloadMissingFiles() and not unknowns.isEmpty())
+	{
+		PartDownloader dl (m_window);
+		dl.setSourceType (PartDownloader::PartsTracker);
+		dl.setPrimaryFile (file);
+
+		for (QString const& unknown : unknowns)
+			dl.downloadFromPartsTracker (unknown);
+
+		dl.exec();
+		dl.checkIfFinished();
+		file->reloadAllSubfiles();
+	}
+}
+
+LDDocument* DocumentManager::findDocumentByName (QString name)
+{
+	for (DocumentMapIterator it (m_documents); it.hasNext();)
+	{
+		LDDocument* document = it.next().value();
+
+		if (isOneOf (name, document->name(), document->defaultName()))
+			return document;
+	}
+
+	return nullptr;
+}
+
+QString Dirname (QString path)
+{
+	int lastpos = path.lastIndexOf (DIRSLASH);
+
+	if (lastpos > 0)
+		return path.left (lastpos);
+
+#ifndef _WIN32
+	if (path[0] == DIRSLASH_CHAR)
+		return DIRSLASH;
+#endif // _WIN32
+
+	return "";
+}
+
+QString Basename (QString path)
+{
+	int lastpos = path.lastIndexOf (DIRSLASH);
+
+	if (lastpos != -1)
+		return path.mid (lastpos + 1);
+
+	return path;
+}
+
+QString DocumentManager::findDocumentPath (QString relativePath, bool subdirs)
+{
+	QString fullPath;
+
+	// LDraw models use backslashes as path separators. Replace those into forward slashes for Qt.
+	relativePath.replace ("\\", "/");
+
+	// Try find it relative to other currently open documents. We want a file in the immediate vicinity of a current
+	// part model to override stock LDraw stuff.
+	QString reltop = Basename (Dirname (relativePath));
+
+	for (LDDocument* doc : m_window->allDocuments())
+	{
+		QString partpath = format ("%1/%2", Dirname (doc->fullPath()), relativePath);
+		QFile f (partpath);
+
+		if (f.exists())
+		{
+			// ensure we don't mix subfiles and 48-primitives with non-subfiles and non-48
+			QString proptop = Basename (Dirname (partpath));
+
+			bool bogus = false;
+
+			for (QString s : g_specialSubdirectories)
+			{
+				if ((proptop == s and reltop != s) or (reltop == s and proptop != s))
+				{
+					bogus = true;
+					break;
+				}
+			}
+
+			if (not bogus)
+				return partpath;
+		}
+	}
+
+	if (QFile::exists (relativePath))
+		return relativePath;
+
+	// Try with just the LDraw path first
+	fullPath = format ("%1" DIRSLASH "%2", m_window->configBag()->lDrawPath(), relativePath);
+
+	if (QFile::exists (fullPath))
+		return fullPath;
+
+	if (subdirs)
+	{
+		// Look in sub-directories: parts and p. Also look in the download path, since that's where we download parts
+		// from the PT to.
+		QStringList dirs = { m_window->configBag()->lDrawPath(), m_window->configBag()->downloadFilePath() };
+		for (const QString& topdir : dirs)
+		{
+			for (const QString& subdir : QStringList ({ "parts", "p" }))
+			{
+				fullPath = format ("%1" DIRSLASH "%2" DIRSLASH "%3", topdir, subdir, relativePath);
+
+				if (QFile::exists (fullPath))
+					return fullPath;
+			}
+		}
+	}
+
+	// Did not find the file.
+	return "";
+}
+
+QFile* DocumentManager::openLDrawFile (QString relpath, bool subdirs, QString* pathpointer)
+{
+	print ("Opening %1...\n", relpath);
+	QString path = findDocumentPath (relpath, subdirs);
+
+	if (pathpointer)
+		*pathpointer = path;
+
+	if (path.isEmpty())
+		return nullptr;
+
+	QFile* fp = new QFile (path);
+
+	if (fp->open (QIODevice::ReadOnly))
+		return fp;
+
+	fp->deleteLater();
+	return nullptr;
+}
+
+LDObjectList DocumentManager::loadFileContents (QFile* fp, int* numWarnings, bool* ok)
+{
+	LDObjectList objs;
+
+	if (numWarnings)
+		*numWarnings = 0;
+
+	DocumentLoader* loader = new DocumentLoader (g_loadingMainFile);
+	loader->read (fp);
+	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 (not loader->isDone())
+		qApp->processEvents();
+
+	// If we wanted the success value, supply that now
+	if (ok)
+		*ok = not loader->hasAborted();
+
+	objs = loader->objects();
+	delete loader;
+	return objs;
+}
+
+LDDocument* DocumentManager::openDocument (QString path, bool search, bool implicit, LDDocument* fileToOverride, bool* aborted)
+{
+	// Convert the file name to lowercase when searching because some parts contain subfile
+	// subfile references with uppercase file names. I'll assume here that the library will always
+	// use lowercase file names for the part files.
+	QFile* fp;
+	QString fullpath;
+
+	if (search)
+	{
+		fp = openLDrawFile (path.toLower(), true, &fullpath);
+	}
+	else
+	{
+		fp = new QFile (path);
+		fullpath = path;
+
+		if (not fp->open (QIODevice::ReadOnly))
+		{
+			delete fp;
+			return nullptr;
+		}
+	}
+
+	if (not fp)
+		return nullptr;
+
+	LDDocument* load = (fileToOverride ? fileToOverride : m_window->newDocument (implicit));
+	load->setFullPath (fullpath);
+	load->setName (LDDocument::shortenName (load->fullPath()));
+
+	// Loading the file shouldn't count as actual edits to the document.
+	load->history()->setIgnoring (true);
+
+	int numWarnings;
+	bool ok;
+	LDObjectList objs = loadFileContents (fp, &numWarnings, &ok);
+	fp->close();
+	fp->deleteLater();
+
+	if (aborted)
+		*aborted = ok == false;
+
+	if (not ok)
+	{
+		load->close();
+		return nullptr;
+	}
+
+	load->addObjects (objs);
+
+	if (g_loadingMainFile)
+	{
+		m_window->changeDocument (load);
+		m_window->renderer()->setDocument (load);
+		print (tr ("File %1 parsed successfully (%2 errors)."), path, numWarnings);
+	}
+
+	load->history()->setIgnoring (false);
+	return load;
+}
+
+void DocumentManager::closeAllDocuments()
+{
+	for (LDDocument* file : m_window->allDocuments())
+		file->close();
+}
+
+void DocumentManager::addRecentFile (QString path)
+{
+	QStringList recentFiles = m_window->configBag()->recentFiles();
+	int idx = recentFiles.indexOf (path);
+
+	// If this file already is in the list, pop it out.
+	if (idx != -1)
+	{
+		if (idx == recentFiles.size() - 1)
+			return; // first recent file - abort and do nothing
+
+		recentFiles.removeAt (idx);
+	}
+
+	// If there's too many recent files, drop one out.
+	while (recentFiles.size() > (MAX_RECENT_FILES - 1))
+		recentFiles.removeAt (0);
+
+	// Add the file
+	recentFiles << path;
+	m_window->configBag()->setRecentFiles (recentFiles);
+	m_window->syncSettings();
+	m_window->updateRecentFilesMenu();
+}
+
+bool DocumentManager::isSafeToCloseAll()
+{
+	for (LDDocument* f : m_window->allDocuments())
+	{
+		if (not f->isSafeToClose())
+			return false;
+	}
+
+	return true;
+}
+
+void DocumentManager::loadLogoedStuds()
+{
+	if (g_loadingLogoedStuds or (g_logoedStud and g_logoedStud2))
+		return;
+
+	g_loadingLogoedStuds = true;
+	g_logoedStud = openDocument ("stud-logo.dat", true, true);
+	g_logoedStud2 = openDocument ("stud2-logo.dat", true, true);
+	print (tr ("Logoed studs loaded.\n"));
+	g_loadingLogoedStuds = false;
+}
+
+bool DocumentManager::preInline (LDDocument* doc, LDObjectList& objs)
+{
+	// Possibly substitute with logoed studs:
+	// stud.dat -> stud-logo.dat
+	// stud2.dat -> stud-logo2.dat
+	if (m_config->useLogoStuds() and renderinline)
+	{
+		// Ensure logoed studs are loaded first
+		loadLogoedStuds();
+
+		if (doc->name() == "stud.dat" and g_logoedStud)
+			return g_logoedStud->inlineContents (deep, renderinline);
+		else if (doc->name() == "stud2.dat" and g_logoedStud2)
+			return g_logoedStud2->inlineContents (deep, renderinline);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/documentmanager.h	Tue Sep 22 23:38:19 2015 +0300
@@ -0,0 +1,57 @@
+/*
+ *  LDForge: LDraw parts authoring CAD
+ *  Copyright (C) 2013 - 2015 Teemu 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/>.
+ */
+
+#pragma once
+#include "main.h"
+#include "hierarchyelement.h"
+
+class DocumentManager : public QObject, public HierarchyElement
+{
+	Q_OBJECT
+
+public:
+	using DocumentMap = QHash<QString, LDDocument*>;
+	using DocumentMapIterator = QHashIterator<QString, LDDocument*>;
+
+	DocumentManager (QObject* parent = nullptr);
+	~DocumentManager();
+
+	void addRecentFile (QString path);
+	void clear();
+	void closeAllDocuments();
+	LDDocument* findDocumentByName (QString name);
+	QString findDocumentPath (QString relpath, bool subdirs);
+	LDDocument* getDocumentByName (QString filename);
+	bool isSafeToCloseAll();
+	void loadLogoedStuds();
+	LDDocument* openDocument (QString path, bool search, bool implicit, LDDocument* fileToOverride, bool* aborted);
+	QFile* openLDrawFile (QString relpath, bool subdirs, QString* pathpointer);
+	void openMainModel (QString path);
+	bool preInline (LDDocument* doc, LDObjectList& objs);
+
+private:
+	DocumentMap m_documents;
+	bool g_loadingMainFile;
+	bool g_loadingLogoedStuds;
+	LDDocument* g_logoedStud;
+	LDDocument* g_logoedStud2;
+
+	LDObjectList loadFileContents (QFile* fp, int* numWarnings, bool* ok);
+};
+
+static const QStringList g_specialSubdirectories ({ "s", "48", "8" });
\ No newline at end of file
--- a/src/hierarchyelement.cpp	Sun Sep 06 20:45:51 2015 +0300
+++ b/src/hierarchyelement.cpp	Tue Sep 22 23:38:19 2015 +0300
@@ -24,6 +24,7 @@
 	}
 
 	m_config = m_window->configBag();
+	m_documents = m_window->documents();
 }
 
 GuiUtilities* HierarchyElement::guiUtilities() const
--- a/src/hierarchyelement.h	Sun Sep 06 20:45:51 2015 +0300
+++ b/src/hierarchyelement.h	Tue Sep 22 23:38:19 2015 +0300
@@ -6,6 +6,7 @@
 class ConfigurationValueBag;
 class GuiUtilities;
 class LDDocument;
+class DocumentManager;
 
 //
 // Objects that are to take part in the MainWindow's hierarchy multiple-inherit from this class to get a few useful
@@ -23,4 +24,5 @@
 protected:
 	MainWindow* m_window;
 	ConfigurationValueBag* m_config;
+	DocumentManager* m_documents;
 };
--- a/src/ldDocument.cpp	Sun Sep 06 20:45:51 2015 +0300
+++ b/src/ldDocument.cpp	Tue Sep 22 23:38:19 2015 +0300
@@ -36,14 +36,7 @@
 ConfigOption (QStringList RecentFiles)
 ConfigOption (bool TryDownloadMissingFiles = false)
 
-static bool g_loadingMainFile = false;
-enum { MAX_RECENT_FILES = 10 };
-static LDDocument* g_logoedStud;
-static LDDocument* g_logoedStud2;
-static bool g_loadingLogoedStuds = false;
-const QStringList g_specialSubdirectories ({ "s", "48", "8" });
-
-LDDocument::LDDocument (QObject* parent) :
+LDDocument::LDDocument (DocumentManager* parent) :
 	QObject (parent),
 	HierarchyElement (parent),
 	m_history (new EditHistory (this)),
@@ -51,7 +44,8 @@
 	m_verticesOutdated (true),
 	m_needVertexMerge (true),
 	m_beingDestroyed (false),
-	m_gldata (new LDGLData)
+	m_gldata (new LDGLData),
+	m_manager (parent)
 {
 	setSavePosition (-1);
 	setTabIndex (-1);
@@ -60,6 +54,9 @@
 
 LDDocument::~LDDocument()
 {
+	for (int i = 0; i < m_objects.size(); ++i)
+		delete m_objects[i];
+
 	m_beingDestroyed = true;
 	delete m_history;
 	delete m_gldata;
@@ -195,232 +192,6 @@
 
 // =============================================================================
 //
-LDDocument* FindDocument (QString name)
-{
-	for (LDDocument* document : g_win->allDocuments())
-	{
-		if (isOneOf (name, document->name(), document->defaultName()))
-			return document;
-	}
-
-	return nullptr;
-}
-
-// =============================================================================
-//
-QString Dirname (QString path)
-{
-	long lastpos = path.lastIndexOf (DIRSLASH);
-
-	if (lastpos > 0)
-		return path.left (lastpos);
-
-#ifndef _WIN32
-	if (path[0] == DIRSLASH_CHAR)
-		return DIRSLASH;
-#endif // _WIN32
-
-	return "";
-}
-
-// =============================================================================
-//
-QString Basename (QString path)
-{
-	long lastpos = path.lastIndexOf (DIRSLASH);
-
-	if (lastpos != -1)
-		return path.mid (lastpos + 1);
-
-	return path;
-}
-
-// =============================================================================
-//
-static QString FindDocumentPath (QString relpath, bool subdirs)
-{
-	QString fullPath;
-
-	// LDraw models use backslashes as path separators. Replace those into forward slashes for Qt.
-	relpath.replace ("\\", "/");
-
-	// Try find it relative to other currently open documents. We want a file in the immediate vicinity of a current
-	// part model to override stock LDraw stuff.
-	QString reltop = Basename (Dirname (relpath));
-
-	for (LDDocument* doc : g_win->allDocuments())
-	{
-		QString partpath = format ("%1/%2", Dirname (doc->fullPath()), relpath);
-		QFile f (partpath);
-
-		if (f.exists())
-		{
-			// ensure we don't mix subfiles and 48-primitives with non-subfiles and non-48
-			QString proptop = Basename (Dirname (partpath));
-
-			bool bogus = false;
-
-			for (QString s : g_specialSubdirectories)
-			{
-				if ((proptop == s and reltop != s) or (reltop == s and proptop != s))
-				{
-					bogus = true;
-					break;
-				}
-			}
-
-			if (not bogus)
-				return partpath;
-		}
-	}
-
-	if (QFile::exists (relpath))
-		return relpath;
-
-	// Try with just the LDraw path first
-	fullPath = format ("%1" DIRSLASH "%2", g_win->configBag()->lDrawPath(), relpath);
-
-	if (QFile::exists (fullPath))
-		return fullPath;
-
-	if (subdirs)
-	{
-		// Look in sub-directories: parts and p. Also look in the download path, since that's where we download parts
-		// from the PT to.
-		QStringList dirs = { g_win->configBag()->lDrawPath(), g_win->configBag()->downloadFilePath() };
-		for (const QString& topdir : dirs)
-		{
-			for (const QString& subdir : QStringList ({ "parts", "p" }))
-			{
-				fullPath = format ("%1" DIRSLASH "%2" DIRSLASH "%3", topdir, subdir, relpath);
-
-				if (QFile::exists (fullPath))
-					return fullPath;
-			}
-		}
-	}
-
-	// Did not find the file.
-	return "";
-}
-
-// =============================================================================
-//
-QFile* OpenLDrawFile (QString relpath, bool subdirs, QString* pathpointer)
-{
-	print ("Opening %1...\n", relpath);
-	QString path = FindDocumentPath (relpath, subdirs);
-
-	if (pathpointer)
-		*pathpointer = path;
-
-	if (path.isEmpty())
-		return nullptr;
-
-	QFile* fp = new QFile (path);
-
-	if (fp->open (QIODevice::ReadOnly))
-		return fp;
-
-	fp->deleteLater();
-	return nullptr;
-}
-
-// =============================================================================
-//
-LDObjectList LoadFileContents (QFile* fp, int* numWarnings, bool* ok)
-{
-	LDObjectList objs;
-
-	if (numWarnings)
-		*numWarnings = 0;
-
-	DocumentLoader* loader = new DocumentLoader (g_loadingMainFile);
-	loader->read (fp);
-	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 (not loader->isDone())
-		qApp->processEvents();
-
-	// If we wanted the success value, supply that now
-	if (ok)
-		*ok = not loader->hasAborted();
-
-	objs = loader->objects();
-	delete loader;
-	return objs;
-}
-
-// =============================================================================
-//
-LDDocument* OpenDocument (QString path, bool search, bool implicit, LDDocument* fileToOverride, bool* aborted)
-{
-	// Convert the file name to lowercase when searching because some parts contain subfile
-	// subfile references with uppercase file names. I'll assume here that the library will always
-	// use lowercase file names for the part files.
-	QFile* fp;
-	QString fullpath;
-
-	if (search)
-	{
-		fp = OpenLDrawFile (path.toLower(), true, &fullpath);
-	}
-	else
-	{
-		fp = new QFile (path);
-		fullpath = path;
-
-		if (not fp->open (QIODevice::ReadOnly))
-		{
-			delete fp;
-			return nullptr;
-		}
-	}
-
-	if (not fp)
-		return nullptr;
-
-	LDDocument* load = (fileToOverride ? fileToOverride : g_win->newDocument (implicit));
-	load->setFullPath (fullpath);
-	load->setName (LDDocument::shortenName (load->fullPath()));
-
-	// Loading the file shouldn't count as actual edits to the document.
-	load->history()->setIgnoring (true);
-
-	int numWarnings;
-	bool ok;
-	LDObjectList objs = LoadFileContents (fp, &numWarnings, &ok);
-	fp->close();
-	fp->deleteLater();
-
-	if (aborted)
-		*aborted = ok == false;
-
-	if (not ok)
-	{
-		load->close();
-		return nullptr;
-	}
-
-	load->addObjects (objs);
-
-	if (g_loadingMainFile)
-	{
-		g_win->changeDocument (load);
-		g_win->renderer()->setDocument (load);
-		print (QObject::tr ("File %1 parsed successfully (%2 errors)."), path, numWarnings);
-	}
-
-	load->history()->setIgnoring (false);
-	return load;
-}
-
-// =============================================================================
-//
 // Performs safety checks. Do this before closing any files!
 //
 bool LDDocument::isSafeToClose()
@@ -479,125 +250,6 @@
 
 // =============================================================================
 //
-void CloseAllDocuments()
-{
-	for (LDDocument* file : g_win->allDocuments())
-		file->close();
-}
-
-// =============================================================================
-//
-void AddRecentFile (QString path)
-{
-	QStringList recentFiles = g_win->configBag()->recentFiles();
-	int idx = recentFiles.indexOf (path);
-
-	// If this file already is in the list, pop it out.
-	if (idx != -1)
-	{
-		if (idx == recentFiles.size() - 1)
-			return; // first recent file - abort and do nothing
-
-		recentFiles.removeAt (idx);
-	}
-
-	// If there's too many recent files, drop one out.
-	while (recentFiles.size() > (MAX_RECENT_FILES - 1))
-		recentFiles.removeAt (0);
-
-	// Add the file
-	recentFiles << path;
-	g_win->configBag()->setRecentFiles (recentFiles);
-	g_win->syncSettings();
-	g_win->updateRecentFilesMenu();
-}
-
-// =============================================================================
-// Open an LDraw file and set it as the main model
-// =============================================================================
-void OpenMainModel (QString path)
-{
-	// If there's already a file with the same name, this file must replace it.
-	LDDocument* documentToReplace = nullptr;
-	LDDocument* file = nullptr;
-	QString shortName = LDDocument::shortenName (path);
-
-	for (LDDocument* doc : g_win->allDocuments())
-	{
-		if (doc->name() == shortName)
-		{
-			documentToReplace = doc;
-			break;
-		}
-	}
-
-	// We cannot open this file if the document this would replace is not
-	// safe to close.
-	if (documentToReplace and not documentToReplace->isSafeToClose())
-		return;
-
-	g_loadingMainFile = true;
-
-	// If we're replacing an existing document, clear the document and
-	// make it ready for being loaded to.
-	if (documentToReplace)
-	{
-		file = documentToReplace;
-		file->clear();
-	}
-
-	bool aborted;
-	file = OpenDocument (path, false, false, file, &aborted);
-
-	if (file == nullptr)
-	{
-		if (not aborted)
-		{
-			// Tell the user loading failed.
-			setlocale (LC_ALL, "C");
-			Critical (format (QObject::tr ("Failed to open %1: %2"), path, strerror (errno)));
-		}
-
-		g_loadingMainFile = false;
-		return;
-	}
-
-	file->openForEditing();
-	g_win->closeInitialDocument();
-	g_win->changeDocument (file);
-	g_win->doFullRefresh();
-	AddRecentFile (path);
-	g_loadingMainFile = false;
-
-	// If there were problems loading subfile references, try see if we can find these
-	// files on the parts tracker.
-	QStringList unknowns;
-
-	for (LDObject* obj : file->objects())
-	{
-		if (obj->type() != OBJ_Error or static_cast<LDError*> (obj)->fileReferenced().isEmpty())
-			continue;
-
-		unknowns << static_cast<LDError*> (obj)->fileReferenced();
-	}
-
-	if (g_win->configBag()->tryDownloadMissingFiles() and not unknowns.isEmpty())
-	{
-		PartDownloader dl (g_win);
-		dl.setSourceType (PartDownloader::PartsTracker);
-		dl.setPrimaryFile (file);
-
-		for (QString const& unknown : unknowns)
-			dl.downloadFromPartsTracker (unknown);
-
-		dl.exec();
-		dl.checkIfFinished();
-		file->reloadAllSubfiles();
-	}
-}
-
-// =============================================================================
-//
 bool LDDocument::save (QString path, int64* sizeptr)
 {
 	if (isCache())
@@ -900,20 +552,6 @@
 
 // =============================================================================
 //
-LDDocument* GetDocument (QString filename)
-{
-	// Try find the file in the list of loaded files
-	LDDocument* doc = FindDocument (filename);
-
-	// If it's not loaded, try open it
-	if (not doc)
-		doc = OpenDocument (filename, true, true);
-
-	return doc;
-}
-
-// =============================================================================
-//
 void LDDocument::reloadAllSubfiles()
 {
 	print ("Reloading subfiles of %1", getDisplayName());
@@ -1026,19 +664,6 @@
 
 // =============================================================================
 //
-bool IsSafeToCloseAll()
-{
-	for (LDDocument* f : g_win->allDocuments())
-	{
-		if (not f->isSafeToClose())
-			return false;
-	}
-
-	return true;
-}
-
-// =============================================================================
-//
 void LDDocument::setObject (int idx, LDObject* obj)
 {
 	if (idx < 0 or idx >= m_objects.size())
@@ -1169,21 +794,10 @@
 // -----------------------------------------------------------------------------
 LDObjectList LDDocument::inlineContents (bool deep, bool renderinline)
 {
-	// Possibly substitute with logoed studs:
-	// stud.dat -> stud-logo.dat
-	// stud2.dat -> stud-logo2.dat
-	if (m_config->useLogoStuds() and renderinline)
-	{
-		// Ensure logoed studs are loaded first
-		LoadLogoStuds();
+	LDObjectList objs, objcache;
 
-		if (name() == "stud.dat" and g_logoedStud)
-			return g_logoedStud->inlineContents (deep, renderinline);
-		else if (name() == "stud2.dat" and g_logoedStud2)
-			return g_logoedStud2->inlineContents (deep, renderinline);
-	}
-
-	LDObjectList objs, objcache;
+	if (m_manager->preInline (this, objs))
+		return objs; // Manager dealt with this inline
 
 	for (LDObject* obj : objects())
 	{
@@ -1207,20 +821,6 @@
 
 // =============================================================================
 //
-void LoadLogoStuds()
-{
-	if (g_loadingLogoedStuds or (g_logoedStud and g_logoedStud2))
-		return;
-
-	g_loadingLogoedStuds = true;
-	g_logoedStud = OpenDocument ("stud-logo.dat", true, true);
-	g_logoedStud2 = OpenDocument ("stud2-logo.dat", true, true);
-	print (QObject::tr ("Logoed studs loaded.\n"));
-	g_loadingLogoedStuds = false;
-}
-
-// =============================================================================
-//
 void LDDocument::addToSelection (LDObject* obj) // [protected]
 {
 	if (obj->isSelected() and obj->document() == this)
--- a/src/ldDocument.h	Sun Sep 06 20:45:51 2015 +0300
+++ b/src/ldDocument.h	Tue Sep 22 23:38:19 2015 +0300
@@ -27,6 +27,7 @@
 class OpenProgressDialog;
 struct LDGLData;
 class GLCompiler;
+class DocumentManager;
 
 //
 // This class stores a document either as a editable file for the user or for
@@ -43,7 +44,7 @@
 	Q_OBJECT
 
 public:
-	LDDocument (QObject* parent);
+	LDDocument (DocumentManager* parent);
 	~LDDocument();
 
 	void addHistoryStep();
@@ -117,31 +118,12 @@
 	QVector<Vertex> m_vertices;
 	LDObjectList m_sel;
 	LDGLData* m_gldata;
+	DocumentManager* m_manager;
 };
 
-// Opens the given file as the main file. Everything is closed first.
-void OpenMainModel (QString path);
-
-// Finds an OpenFile by name or null if not open
-LDDocument* FindDocument (QString name);
-
-// Opens the given file and parses the LDraw code within. Returns a pointer
-// to the opened file or null on error.
-LDDocument* OpenDocument (QString path, bool search, bool implicit, LDDocument* fileToOverride = nullptr, bool* aborted = nullptr);
-
-// Opens the given file and returns a pointer to it, potentially looking in /parts and /p
-QFile* OpenLDrawFile (QString relpath, bool subdirs, QString* pathpointer = nullptr);
-
-// Close all open files, whether user-opened or subfile caches.
-void CloseAllDocuments();
-
 // Parses a string line containing an LDraw object and returns the object parsed.
 LDObject* ParseLine (QString line);
 
-// Retrieves the pointer to the given document by file name. Document is loaded
-// from file if necessary. Can return null if neither succeeds.
-LDDocument* GetDocument (QString filename);
-
 // Is it safe to close all files?
 bool IsSafeToCloseAll();
 
--- a/src/mainwindow.cpp	Sun Sep 06 20:45:51 2015 +0300
+++ b/src/mainwindow.cpp	Tue Sep 22 23:38:19 2015 +0300
@@ -54,6 +54,7 @@
 #include "dialogs/configdialog.h"
 #include "guiutilities.h"
 #include "glCompiler.h"
+#include "documentmanager.h"
 
 static bool g_isSelectionLocked = false;
 static QMap<QAction*, QKeySequence> g_defaultShortcuts;
@@ -72,6 +73,7 @@
 	ui (*new Ui_MainWindow),
 	m_externalPrograms (nullptr),
 	m_settings (makeSettings (this)),
+	m_documents (new DocumentManager (this)),
 	m_currentDocument (nullptr)
 {
 	g_win = this;
@@ -1132,7 +1134,7 @@
 
 // ---------------------------------------------------------------------------------------------------------------------
 //
-QKeySequence MainWindow::defaultShortcut (QAction* act) // [static]
+QKeySequence MainWindow::defaultShortcut (QAction* act)
 {
 	return g_defaultShortcuts[act];
 }
--- a/src/mainwindow.h	Sun Sep 06 20:45:51 2015 +0300
+++ b/src/mainwindow.h	Tue Sep 22 23:38:19 2015 +0300
@@ -79,9 +79,10 @@
 	void createBlankDocument();
 	LDDocument* currentDocument();
 	void currentDocumentClosed();
-	static QKeySequence defaultShortcut (QAction* act);
+	QKeySequence defaultShortcut (QAction* act);
 	void deleteByColor (LDColor color);
 	int deleteSelection();
+	DocumentManager* documents() { return m_documents; }
 	void doFullRefresh();
 	void endAction();
 	class ExtProgramToolset* externalPrograms();
@@ -145,7 +146,7 @@
 	QMap<QAction*, ToolInfo> m_toolmap;
 	class ExtProgramToolset* m_externalPrograms;
 	class QSettings* m_settings;
-	QList<LDDocument*> m_documents;
+	DocumentManager* m_documents;
 	LDDocument* m_currentDocument;
 	DoubleMap<LDObject*, QListWidgetItem*> m_objectsInList;
 

mercurial