Tue, 14 Feb 2017 14:52:01 +0200
Renamed ldDocument.cpp → lddocument.cpp
--- a/CMakeLists.txt Tue Feb 14 14:51:04 2017 +0200 +++ b/CMakeLists.txt Tue Feb 14 14:52:01 2017 +0200 @@ -41,7 +41,7 @@ src/grid.cpp src/guiutilities.cpp src/hierarchyelement.cpp - src/ldDocument.cpp + src/lddocument.cpp src/ldObject.cpp src/ldpaths.cpp src/main.cpp @@ -103,7 +103,7 @@ src/grid.h src/guiutilities.h src/hierarchyelement.h - src/ldDocument.h + src/lddocument.h src/ldObject.h src/ldobjectiterator.h src/ldpaths.h
--- a/src/basics.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/basics.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -18,7 +18,7 @@ #include "miscallenous.h" #include "ldObject.h" -#include "ldDocument.h" +#include "lddocument.h" Vertex::Vertex() : QVector3D() {}
--- a/src/canvas.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/canvas.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -21,7 +21,7 @@ #include "documentmanager.h" #include "glcamera.h" #include "grid.h" -#include "ldDocument.h" +#include "lddocument.h" #include "mainwindow.h" #include "messageLog.h" #include "miscallenous.h"
--- a/src/dialogs/configdialog.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/dialogs/configdialog.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -32,7 +32,7 @@ #include <QSettings> #include <QPushButton> #include "../main.h" -#include "../ldDocument.h" +#include "../lddocument.h" #include "../miscallenous.h" #include "../canvas.h" #include "../guiutilities.h"
--- a/src/dialogs/newpartdialog.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/dialogs/newpartdialog.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -19,7 +19,7 @@ #include <QLineEdit> #include <QRadioButton> #include <QCheckBox> -#include "../ldDocument.h" +#include "../lddocument.h" #include "../mainwindow.h" #include "newpartdialog.h" #include "ui_newpartdialog.h"
--- a/src/documentloader.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/documentloader.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -17,7 +17,7 @@ */ #include "documentloader.h" -#include "ldDocument.h" +#include "lddocument.h" #include "ldObject.h" #include "mainwindow.h" #include "dialogs/openprogressdialog.h" @@ -77,7 +77,7 @@ if (isOnForeground()) { - // Show a progress dialog if we're loading the main ldDocument.here so we can show progress updates and keep the + // Show a progress dialog if we're loading the main lddocument.here so we can show progress updates and keep the // WM posted that we're still here. m_progressDialog = new OpenProgressDialog(qobject_cast<QWidget*>(parent())); m_progressDialog->setNumLines (countof(m_lines));
--- a/src/documentmanager.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/documentmanager.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -19,7 +19,7 @@ #include <QApplication> #include <QFileInfo> #include "documentmanager.h" -#include "ldDocument.h" +#include "lddocument.h" #include "mainwindow.h" #include "partdownloader.h" #include "documentloader.h"
--- a/src/editHistory.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/editHistory.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -18,7 +18,7 @@ #include "editHistory.h" #include "ldObject.h" -#include "ldDocument.h" +#include "lddocument.h" #include "miscallenous.h" #include "mainwindow.h" #include "glrenderer.h"
--- a/src/editmodes/abstractEditMode.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/editmodes/abstractEditMode.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -27,7 +27,7 @@ #include "linePathMode.h" #include "curvemode.h" #include "../mainwindow.h" -#include "../ldDocument.h" +#include "../lddocument.h" #include "../canvas.h" #include "../miscallenous.h" #include "../grid.h"
--- a/src/editmodes/circleMode.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/editmodes/circleMode.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -20,7 +20,7 @@ #include "circleMode.h" #include "../miscallenous.h" #include "../ldObject.h" -#include "../ldDocument.h" +#include "../lddocument.h" #include "../ringFinder.h" #include "../primitives.h" #include "../canvas.h"
--- a/src/editmodes/magicWandMode.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/editmodes/magicWandMode.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -18,7 +18,7 @@ #include <QMouseEvent> #include "magicWandMode.h" -#include "../ldDocument.h" +#include "../lddocument.h" #include "../mainwindow.h" #include "../canvas.h"
--- a/src/editmodes/selectMode.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/editmodes/selectMode.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -20,7 +20,7 @@ #include "selectMode.h" #include "../canvas.h" #include "../mainwindow.h" -#include "../ldDocument.h" +#include "../lddocument.h" SelectMode::SelectMode (Canvas* canvas) : Super (canvas),
--- a/src/glrenderer.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/glrenderer.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -24,7 +24,7 @@ #include <QTimer> #include <GL/glu.h> #include "main.h" -#include "ldDocument.h" +#include "lddocument.h" #include "glrenderer.h" #include "colors.h" #include "mainwindow.h"
--- a/src/guiutilities.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/guiutilities.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -20,7 +20,7 @@ #include <QPainter> #include "colors.h" #include "guiutilities.h" -#include "ldDocument.h" +#include "lddocument.h" #include "mainwindow.h" GuiUtilities::GuiUtilities (QObject* parent) :
--- a/src/ldDocument.cpp Tue Feb 14 14:51:04 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,528 +0,0 @@ -/* - * LDForge: LDraw parts authoring CAD - * Copyright (C) 2013 - 2017 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 <QMessageBox> -#include <QFileDialog> -#include "ldDocument.h" -#include "miscallenous.h" -#include "mainwindow.h" -#include "canvas.h" -#include "documentloader.h" -#include "dialogs/openprogressdialog.h" -#include "documentmanager.h" -#include "linetypes/comment.h" - -LDDocument::LDDocument (DocumentManager* parent) : - Model {parent}, - HierarchyElement (parent), - m_history (new EditHistory (this)), - m_savePosition(-1), - m_tabIndex(-1), - m_manager (parent) {} - -LDDocument::~LDDocument() -{ - m_isBeingDestroyed = true; - delete m_history; -} - -QString LDDocument::name() const -{ - return m_name; -} - -void LDDocument::setName (QString value) -{ - m_name = value; -} - -EditHistory* LDDocument::history() const -{ - return m_history; -} - -QString LDDocument::fullPath() -{ - return m_fullPath; -} - -void LDDocument::setFullPath (QString value) -{ - m_fullPath = value; -} - -int LDDocument::tabIndex() const -{ - return m_tabIndex; -} - -void LDDocument::setTabIndex (int value) -{ - m_tabIndex = value; -} - -const QList<LDPolygon>& LDDocument::polygonData() const -{ - return m_polygonData; -} - -long LDDocument::savePosition() const -{ - return m_savePosition; -} - -void LDDocument::setSavePosition (long value) -{ - m_savePosition = value; -} - -QString LDDocument::defaultName() const -{ - return m_defaultName; -} - -void LDDocument::setDefaultName (QString value) -{ - m_defaultName = value; -} - -void LDDocument::setFrozen(bool value) -{ - m_isFrozen = value; -} - -bool LDDocument::isFrozen() const -{ - return m_isFrozen; -} - -void LDDocument::addHistoryStep() -{ - history()->addStep(); -} - -void LDDocument::undo() -{ - history()->undo(); -} - -void LDDocument::redo() -{ - history()->redo(); -} - -void LDDocument::clearHistory() -{ - history()->clear(); -} - -void LDDocument::addToHistory (AbstractHistoryEntry* entry) -{ - history()->add (entry); -} - -void LDDocument::close() -{ - if (not isFrozen()) - { - setFrozen(true); - m_manager->documentClosed(this); - } -} - -// ============================================================================= -// -// Performs safety checks. Do this before closing any files! -// -bool LDDocument::isSafeToClose() -{ - using msgbox = QMessageBox; - setlocale (LC_ALL, "C"); - - // If we have unsaved changes, warn and give the option of saving. - if (hasUnsavedChanges()) - { - QString message = format (tr ("There are unsaved changes to %1. Should it be saved?"), getDisplayName()); - - int button = msgbox::question (m_window, QObject::tr ("Unsaved Changes"), message, - (msgbox::Yes | msgbox::No | msgbox::Cancel), msgbox::Cancel); - - switch (button) - { - case msgbox::Yes: - { - // If we don't have a file path yet, we have to ask the user for one. - if (name().isEmpty()) - { - QString newpath = QFileDialog::getSaveFileName (m_window, QObject::tr ("Save As"), - name(), QObject::tr ("LDraw files (*.dat *.ldr)")); - - if (newpath.isEmpty()) - return false; - - setName (newpath); - } - - if (not save()) - { - message = format (QObject::tr ("Failed to save %1 (%2)\nDo you still want to close?"), - name(), strerror (errno)); - - if (msgbox::critical (m_window, QObject::tr ("Save Failure"), message, - (msgbox::Yes | msgbox::No), msgbox::No) == msgbox::No) - { - return false; - } - } - break; - } - - case msgbox::Cancel: - return false; - - default: - break; - } - } - - return true; -} - -// ============================================================================= -// -bool LDDocument::save (QString path, qint64* sizeptr) -{ - if (isFrozen()) - return false; - - if (path.isEmpty()) - path = fullPath(); - - // If the second object in the list holds the file name, update that now. - LDObject* nameObject = getObject (1); - - if (nameObject and nameObject->type() == LDObjectType::Comment) - { - LDComment* nameComment = static_cast<LDComment*> (nameObject); - - if (nameComment->text().left (6) == "Name: ") - { - QString newname = shortenName (path); - nameComment->setText (format ("Name: %1", newname)); - m_window->buildObjectList(); - } - } - - QByteArray data; - - if (sizeptr) - *sizeptr = 0; - - // File is open, now save the model to it. Note that LDraw requires files to have DOS line endings. - for (LDObject* obj : objects()) - { - QByteArray subdata ((obj->asText() + "\r\n").toUtf8()); - data.append (subdata); - - if (sizeptr) - *sizeptr += countof(subdata); - } - - QFile f (path); - - if (not f.open (QIODevice::WriteOnly)) - return false; - - f.write (data); - f.close(); - - // We have successfully saved, update the save position now. - setSavePosition (history()->position()); - setFullPath (path); - setName (shortenName (path)); - m_window->updateDocumentListItem (this); - m_window->updateTitle(); - return true; -} - -// ============================================================================= -// -void LDDocument::reloadAllSubfiles() -{ - print ("Reloading subfiles of %1", getDisplayName()); - - // Go through all objects in the current file and reload the subfiles - for (LDObject* obj : objects()) - { - if (obj->type() == LDObjectType::SubfileReference) - { - LDSubfileReference* reference = static_cast<LDSubfileReference*> (obj); - LDDocument* fileInfo = m_documents->getDocumentByName (reference->fileInfo()->name()); - - if (fileInfo) - reference->setFileInfo (fileInfo); - else - emplaceReplacement<LDError>(reference, reference->asText(), format("Could not open %1", reference->fileInfo()->name())); - } - - // Reparse gibberish files. It could be that they are invalid because - // of loading errors. Circumstances may be different now. - if (obj->type() == LDObjectType::Error) - replaceWithFromString(obj, static_cast<LDError*> (obj)->contents()); - } - - m_needsRecache = true; - - if (this == m_window->currentDocument()) - m_window->buildObjectList(); -} - -// ============================================================================= -// -void LDDocument::insertObject (int pos, LDObject* obj) -{ - Model::insertObject(pos, obj); - history()->add(new AddHistoryEntry {pos, obj}); - connect(obj, SIGNAL(codeChanged(QString,QString)), this, SLOT(objectChanged(QString,QString))); - -#ifdef DEBUG - if (not isFrozen()) - dprint ("Inserted object #%1 (%2) at %3\n", obj->id(), obj->typeName(), pos); -#endif -} - -void LDDocument::objectChanged(QString before, QString after) -{ - LDObject* object = static_cast<LDObject*>(sender()); - addToHistory(new EditHistoryEntry {object->lineNumber(), before, after}); - redoVertices(); - emit objectModified(object); -} - -LDObject* LDDocument::withdrawAt(int position) -{ - LDObject* object = getObject(position); - - if (not isFrozen() and not m_isBeingDestroyed) - { - history()->add(new DelHistoryEntry {position, object}); - m_objectVertices.remove(object); - } - - m_selection.remove(object); - return Model::withdrawAt(position); -} - -// ============================================================================= -// -bool LDDocument::hasUnsavedChanges() const -{ - return not isFrozen() and history()->position() != savePosition(); -} - -// ============================================================================= -// -QString LDDocument::getDisplayName() -{ - if (not name().isEmpty()) - return name(); - - if (not defaultName().isEmpty()) - return "[" + defaultName() + "]"; - - return QObject::tr ("untitled"); -} - -// ============================================================================= -// -void LDDocument::initializeCachedData() -{ - if (m_needsRecache) - { - m_vertices.clear(); - Model model {m_documents}; - inlineContents(model, true, true); - - for (LDObject* obj : model.objects()) - { - if (obj->type() == LDObjectType::SubfileReference) - { - print ("Warning: unable to inline %1 into %2", - static_cast<LDSubfileReference*> (obj)->fileInfo()->getDisplayName(), - getDisplayName()); - continue; - } - - LDPolygon* data = obj->getPolygon(); - - if (data) - { - m_polygonData << *data; - delete data; - } - } - - m_needsRecache = false; - } - - if (m_verticesOutdated) - { - m_objectVertices.clear(); - Model model {m_documents}; - inlineContents(model, true, false); - - for (LDObject* object : model) - { - auto iterator = m_objectVertices.find (object); - - if (iterator == m_objectVertices.end()) - iterator = m_objectVertices.insert (object, QSet<Vertex>()); - else - iterator->clear(); - - object->getVertices (*iterator); - } - - m_vertices.clear(); - - for (const QSet<Vertex>& vertices : m_objectVertices) - m_vertices.unite(vertices); - - m_verticesOutdated = false; - } -} - -// ============================================================================= -// -QList<LDPolygon> LDDocument::inlinePolygons() -{ - initializeCachedData(); - return polygonData(); -} - -// ============================================================================= -// ----------------------------------------------------------------------------- -void LDDocument::inlineContents(Model& model, bool deep, bool renderinline) -{ - if (m_manager->preInline(this, model, deep, renderinline)) - return; // Manager dealt with this inline - - for (LDObject* object : objects()) - { - // Skip those without scemantic meaning - if (not object->isScemantic()) - continue; - - // Got another sub-file reference, inline it if we're deep-inlining. If not, - // just add it into the objects normally. Yay, recursion! - if (deep and object->type() == LDObjectType::SubfileReference) - static_cast<LDSubfileReference*>(object)->inlineContents(model, deep, renderinline); - else - model.addFromString(object->asText()); - } -} - -// ============================================================================= -// -void LDDocument::addToSelection (LDObject* obj) // [protected] -{ - if (not m_selection.contains(obj) and obj->model() == this) - { - m_selection.insert(obj); - emit objectModified(obj); - - // If this object is inverted with INVERTNEXT, select the INVERTNEXT as well. - LDBfc* invertnext; - - if (obj->previousIsInvertnext(invertnext)) - addToSelection(invertnext); - } -} - -// ============================================================================= -// -void LDDocument::removeFromSelection (LDObject* obj) // [protected] -{ - if (m_selection.contains(obj)) - { - m_selection.remove(obj); - emit objectModified(obj); - - // If this object is inverted with INVERTNEXT, deselect the INVERTNEXT as well. - LDBfc* invertnext; - - if (obj->previousIsInvertnext(invertnext)) - removeFromSelection(invertnext); - } -} - -// ============================================================================= -// -void LDDocument::clearSelection() -{ - for (LDObject* object : m_selection.toList()) - removeFromSelection(object); -} - -// ============================================================================= -// -const QSet<LDObject*>& LDDocument::getSelection() const -{ - return m_selection; -} - -// ============================================================================= -// -bool LDDocument::swapObjects (LDObject* one, LDObject* other) -{ - if (Model::swapObjects(one, other)) - { - addToHistory(new SwapHistoryEntry {one->id(), other->id()}); - return true; - } - else - { - return false; - } -} - -// ============================================================================= -// -QString LDDocument::shortenName (QString a) // [static] -{ - QString shortname = Basename (a); - QString topdirname = Basename (Dirname (a)); - - if (DocumentManager::specialSubdirectories.contains (topdirname)) - shortname.prepend (topdirname + "\\"); - - return shortname; -} - -// ============================================================================= -// -const QSet<Vertex>& LDDocument::inlineVertices() -{ - initializeCachedData(); - return m_vertices; -} - -void LDDocument::redoVertices() -{ - m_verticesOutdated = true; -}
--- a/src/ldDocument.h Tue Feb 14 14:51:04 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -/* - * LDForge: LDraw parts authoring CAD - * Copyright (C) 2013 - 2017 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 <QObject> -#include "main.h" -#include "ldObject.h" -#include "editHistory.h" -#include "glShared.h" -#include "model.h" - -struct LDGLData; -class DocumentManager; - -// -// This class stores a document either as a editable file for the user or for -// subfile caching. -// -// A document is implicit when they are opened automatically for caching purposes -// and are hidden from the user. User-opened files are explicit (not implicit). -// -// The default name is a placeholder, initially suggested name for a file. The -// primitive generator uses this to give initial names to primitives. -// -class LDDocument : public Model, public HierarchyElement -{ - Q_OBJECT - -public: - LDDocument (DocumentManager* parent); - ~LDDocument(); - - void addHistoryStep(); - void addToHistory (AbstractHistoryEntry* entry); - void addToSelection (LDObject* obj); - void clearHistory(); - void clearSelection(); - void close(); - QString defaultName() const; - QString fullPath(); - QString getDisplayName(); - const QSet<LDObject*>& getSelection() const; - bool hasUnsavedChanges() const; - EditHistory* history() const; - void initializeCachedData(); - void inlineContents(Model& model, bool deep, bool renderinline); - QList<LDPolygon> inlinePolygons(); - const QSet<Vertex>& inlineVertices(); - void insertObject (int pos, LDObject* obj); - bool isFrozen() const; - bool isSafeToClose(); - QString name() const; - void objectRemoved(LDObject* object, int index); - const QList<LDPolygon>& polygonData() const; - void recountTriangles(); - void redo(); - void redoVertices(); - void reloadAllSubfiles(); - void removeFromSelection (LDObject* obj); - bool save (QString path = "", qint64* sizeptr = nullptr); - long savePosition() const; - void setDefaultName (QString value); - void setFrozen(bool value); - void setFullPath (QString value); - void setName (QString value); - void setSavePosition (long value); - void setTabIndex (int value); - bool swapObjects (LDObject* one, LDObject* other); - int tabIndex() const; - void undo(); - void vertexChanged (const Vertex& a, const Vertex& b); - - static QString shortenName (QString a); // Turns a full path into a relative path - -public slots: - void objectChanged(QString before, QString after); - -protected: - LDObject* withdrawAt(int position); - -private: - QString m_name; - QString m_fullPath; - QString m_defaultName; - EditHistory* m_history; - bool m_isFrozen = true; // Document may not be modified - bool m_verticesOutdated = true; - bool m_isBeingDestroyed = true; - bool m_needsRecache = true; // The next polygon inline of this document rebuilds stored polygon data. - long m_savePosition; - int m_tabIndex; - int m_triangleCount; - QList<LDPolygon> m_polygonData; - QMap<LDObject*, QSet<Vertex>> m_objectVertices; - QSet<Vertex> m_vertices; - QSet<LDObject*> m_selection; - DocumentManager* m_manager; -}; - -// Parses a string line containing an LDraw object and returns the object parsed. -LDObject* ParseLine (QString line);
--- a/src/ldObject.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/ldObject.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -19,7 +19,7 @@ #include <assert.h> #include "documentmanager.h" #include "ldObject.h" -#include "ldDocument.h" +#include "lddocument.h" #include "miscallenous.h" #include "mainwindow.h" #include "editHistory.h"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lddocument.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -0,0 +1,528 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2017 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 <QMessageBox> +#include <QFileDialog> +#include "lddocument.h" +#include "miscallenous.h" +#include "mainwindow.h" +#include "canvas.h" +#include "documentloader.h" +#include "dialogs/openprogressdialog.h" +#include "documentmanager.h" +#include "linetypes/comment.h" + +LDDocument::LDDocument (DocumentManager* parent) : + Model {parent}, + HierarchyElement (parent), + m_history (new EditHistory (this)), + m_savePosition(-1), + m_tabIndex(-1), + m_manager (parent) {} + +LDDocument::~LDDocument() +{ + m_isBeingDestroyed = true; + delete m_history; +} + +QString LDDocument::name() const +{ + return m_name; +} + +void LDDocument::setName (QString value) +{ + m_name = value; +} + +EditHistory* LDDocument::history() const +{ + return m_history; +} + +QString LDDocument::fullPath() +{ + return m_fullPath; +} + +void LDDocument::setFullPath (QString value) +{ + m_fullPath = value; +} + +int LDDocument::tabIndex() const +{ + return m_tabIndex; +} + +void LDDocument::setTabIndex (int value) +{ + m_tabIndex = value; +} + +const QList<LDPolygon>& LDDocument::polygonData() const +{ + return m_polygonData; +} + +long LDDocument::savePosition() const +{ + return m_savePosition; +} + +void LDDocument::setSavePosition (long value) +{ + m_savePosition = value; +} + +QString LDDocument::defaultName() const +{ + return m_defaultName; +} + +void LDDocument::setDefaultName (QString value) +{ + m_defaultName = value; +} + +void LDDocument::setFrozen(bool value) +{ + m_isFrozen = value; +} + +bool LDDocument::isFrozen() const +{ + return m_isFrozen; +} + +void LDDocument::addHistoryStep() +{ + history()->addStep(); +} + +void LDDocument::undo() +{ + history()->undo(); +} + +void LDDocument::redo() +{ + history()->redo(); +} + +void LDDocument::clearHistory() +{ + history()->clear(); +} + +void LDDocument::addToHistory (AbstractHistoryEntry* entry) +{ + history()->add (entry); +} + +void LDDocument::close() +{ + if (not isFrozen()) + { + setFrozen(true); + m_manager->documentClosed(this); + } +} + +// ============================================================================= +// +// Performs safety checks. Do this before closing any files! +// +bool LDDocument::isSafeToClose() +{ + using msgbox = QMessageBox; + setlocale (LC_ALL, "C"); + + // If we have unsaved changes, warn and give the option of saving. + if (hasUnsavedChanges()) + { + QString message = format (tr ("There are unsaved changes to %1. Should it be saved?"), getDisplayName()); + + int button = msgbox::question (m_window, QObject::tr ("Unsaved Changes"), message, + (msgbox::Yes | msgbox::No | msgbox::Cancel), msgbox::Cancel); + + switch (button) + { + case msgbox::Yes: + { + // If we don't have a file path yet, we have to ask the user for one. + if (name().isEmpty()) + { + QString newpath = QFileDialog::getSaveFileName (m_window, QObject::tr ("Save As"), + name(), QObject::tr ("LDraw files (*.dat *.ldr)")); + + if (newpath.isEmpty()) + return false; + + setName (newpath); + } + + if (not save()) + { + message = format (QObject::tr ("Failed to save %1 (%2)\nDo you still want to close?"), + name(), strerror (errno)); + + if (msgbox::critical (m_window, QObject::tr ("Save Failure"), message, + (msgbox::Yes | msgbox::No), msgbox::No) == msgbox::No) + { + return false; + } + } + break; + } + + case msgbox::Cancel: + return false; + + default: + break; + } + } + + return true; +} + +// ============================================================================= +// +bool LDDocument::save (QString path, qint64* sizeptr) +{ + if (isFrozen()) + return false; + + if (path.isEmpty()) + path = fullPath(); + + // If the second object in the list holds the file name, update that now. + LDObject* nameObject = getObject (1); + + if (nameObject and nameObject->type() == LDObjectType::Comment) + { + LDComment* nameComment = static_cast<LDComment*> (nameObject); + + if (nameComment->text().left (6) == "Name: ") + { + QString newname = shortenName (path); + nameComment->setText (format ("Name: %1", newname)); + m_window->buildObjectList(); + } + } + + QByteArray data; + + if (sizeptr) + *sizeptr = 0; + + // File is open, now save the model to it. Note that LDraw requires files to have DOS line endings. + for (LDObject* obj : objects()) + { + QByteArray subdata ((obj->asText() + "\r\n").toUtf8()); + data.append (subdata); + + if (sizeptr) + *sizeptr += countof(subdata); + } + + QFile f (path); + + if (not f.open (QIODevice::WriteOnly)) + return false; + + f.write (data); + f.close(); + + // We have successfully saved, update the save position now. + setSavePosition (history()->position()); + setFullPath (path); + setName (shortenName (path)); + m_window->updateDocumentListItem (this); + m_window->updateTitle(); + return true; +} + +// ============================================================================= +// +void LDDocument::reloadAllSubfiles() +{ + print ("Reloading subfiles of %1", getDisplayName()); + + // Go through all objects in the current file and reload the subfiles + for (LDObject* obj : objects()) + { + if (obj->type() == LDObjectType::SubfileReference) + { + LDSubfileReference* reference = static_cast<LDSubfileReference*> (obj); + LDDocument* fileInfo = m_documents->getDocumentByName (reference->fileInfo()->name()); + + if (fileInfo) + reference->setFileInfo (fileInfo); + else + emplaceReplacement<LDError>(reference, reference->asText(), format("Could not open %1", reference->fileInfo()->name())); + } + + // Reparse gibberish files. It could be that they are invalid because + // of loading errors. Circumstances may be different now. + if (obj->type() == LDObjectType::Error) + replaceWithFromString(obj, static_cast<LDError*> (obj)->contents()); + } + + m_needsRecache = true; + + if (this == m_window->currentDocument()) + m_window->buildObjectList(); +} + +// ============================================================================= +// +void LDDocument::insertObject (int pos, LDObject* obj) +{ + Model::insertObject(pos, obj); + history()->add(new AddHistoryEntry {pos, obj}); + connect(obj, SIGNAL(codeChanged(QString,QString)), this, SLOT(objectChanged(QString,QString))); + +#ifdef DEBUG + if (not isFrozen()) + dprint ("Inserted object #%1 (%2) at %3\n", obj->id(), obj->typeName(), pos); +#endif +} + +void LDDocument::objectChanged(QString before, QString after) +{ + LDObject* object = static_cast<LDObject*>(sender()); + addToHistory(new EditHistoryEntry {object->lineNumber(), before, after}); + redoVertices(); + emit objectModified(object); +} + +LDObject* LDDocument::withdrawAt(int position) +{ + LDObject* object = getObject(position); + + if (not isFrozen() and not m_isBeingDestroyed) + { + history()->add(new DelHistoryEntry {position, object}); + m_objectVertices.remove(object); + } + + m_selection.remove(object); + return Model::withdrawAt(position); +} + +// ============================================================================= +// +bool LDDocument::hasUnsavedChanges() const +{ + return not isFrozen() and history()->position() != savePosition(); +} + +// ============================================================================= +// +QString LDDocument::getDisplayName() +{ + if (not name().isEmpty()) + return name(); + + if (not defaultName().isEmpty()) + return "[" + defaultName() + "]"; + + return QObject::tr ("untitled"); +} + +// ============================================================================= +// +void LDDocument::initializeCachedData() +{ + if (m_needsRecache) + { + m_vertices.clear(); + Model model {m_documents}; + inlineContents(model, true, true); + + for (LDObject* obj : model.objects()) + { + if (obj->type() == LDObjectType::SubfileReference) + { + print ("Warning: unable to inline %1 into %2", + static_cast<LDSubfileReference*> (obj)->fileInfo()->getDisplayName(), + getDisplayName()); + continue; + } + + LDPolygon* data = obj->getPolygon(); + + if (data) + { + m_polygonData << *data; + delete data; + } + } + + m_needsRecache = false; + } + + if (m_verticesOutdated) + { + m_objectVertices.clear(); + Model model {m_documents}; + inlineContents(model, true, false); + + for (LDObject* object : model) + { + auto iterator = m_objectVertices.find (object); + + if (iterator == m_objectVertices.end()) + iterator = m_objectVertices.insert (object, QSet<Vertex>()); + else + iterator->clear(); + + object->getVertices (*iterator); + } + + m_vertices.clear(); + + for (const QSet<Vertex>& vertices : m_objectVertices) + m_vertices.unite(vertices); + + m_verticesOutdated = false; + } +} + +// ============================================================================= +// +QList<LDPolygon> LDDocument::inlinePolygons() +{ + initializeCachedData(); + return polygonData(); +} + +// ============================================================================= +// ----------------------------------------------------------------------------- +void LDDocument::inlineContents(Model& model, bool deep, bool renderinline) +{ + if (m_manager->preInline(this, model, deep, renderinline)) + return; // Manager dealt with this inline + + for (LDObject* object : objects()) + { + // Skip those without scemantic meaning + if (not object->isScemantic()) + continue; + + // Got another sub-file reference, inline it if we're deep-inlining. If not, + // just add it into the objects normally. Yay, recursion! + if (deep and object->type() == LDObjectType::SubfileReference) + static_cast<LDSubfileReference*>(object)->inlineContents(model, deep, renderinline); + else + model.addFromString(object->asText()); + } +} + +// ============================================================================= +// +void LDDocument::addToSelection (LDObject* obj) // [protected] +{ + if (not m_selection.contains(obj) and obj->model() == this) + { + m_selection.insert(obj); + emit objectModified(obj); + + // If this object is inverted with INVERTNEXT, select the INVERTNEXT as well. + LDBfc* invertnext; + + if (obj->previousIsInvertnext(invertnext)) + addToSelection(invertnext); + } +} + +// ============================================================================= +// +void LDDocument::removeFromSelection (LDObject* obj) // [protected] +{ + if (m_selection.contains(obj)) + { + m_selection.remove(obj); + emit objectModified(obj); + + // If this object is inverted with INVERTNEXT, deselect the INVERTNEXT as well. + LDBfc* invertnext; + + if (obj->previousIsInvertnext(invertnext)) + removeFromSelection(invertnext); + } +} + +// ============================================================================= +// +void LDDocument::clearSelection() +{ + for (LDObject* object : m_selection.toList()) + removeFromSelection(object); +} + +// ============================================================================= +// +const QSet<LDObject*>& LDDocument::getSelection() const +{ + return m_selection; +} + +// ============================================================================= +// +bool LDDocument::swapObjects (LDObject* one, LDObject* other) +{ + if (Model::swapObjects(one, other)) + { + addToHistory(new SwapHistoryEntry {one->id(), other->id()}); + return true; + } + else + { + return false; + } +} + +// ============================================================================= +// +QString LDDocument::shortenName (QString a) // [static] +{ + QString shortname = Basename (a); + QString topdirname = Basename (Dirname (a)); + + if (DocumentManager::specialSubdirectories.contains (topdirname)) + shortname.prepend (topdirname + "\\"); + + return shortname; +} + +// ============================================================================= +// +const QSet<Vertex>& LDDocument::inlineVertices() +{ + initializeCachedData(); + return m_vertices; +} + +void LDDocument::redoVertices() +{ + m_verticesOutdated = true; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lddocument.h Tue Feb 14 14:52:01 2017 +0200 @@ -0,0 +1,116 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2017 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 <QObject> +#include "main.h" +#include "ldObject.h" +#include "editHistory.h" +#include "glShared.h" +#include "model.h" + +struct LDGLData; +class DocumentManager; + +// +// This class stores a document either as a editable file for the user or for +// subfile caching. +// +// A document is implicit when they are opened automatically for caching purposes +// and are hidden from the user. User-opened files are explicit (not implicit). +// +// The default name is a placeholder, initially suggested name for a file. The +// primitive generator uses this to give initial names to primitives. +// +class LDDocument : public Model, public HierarchyElement +{ + Q_OBJECT + +public: + LDDocument (DocumentManager* parent); + ~LDDocument(); + + void addHistoryStep(); + void addToHistory (AbstractHistoryEntry* entry); + void addToSelection (LDObject* obj); + void clearHistory(); + void clearSelection(); + void close(); + QString defaultName() const; + QString fullPath(); + QString getDisplayName(); + const QSet<LDObject*>& getSelection() const; + bool hasUnsavedChanges() const; + EditHistory* history() const; + void initializeCachedData(); + void inlineContents(Model& model, bool deep, bool renderinline); + QList<LDPolygon> inlinePolygons(); + const QSet<Vertex>& inlineVertices(); + void insertObject (int pos, LDObject* obj); + bool isFrozen() const; + bool isSafeToClose(); + QString name() const; + void objectRemoved(LDObject* object, int index); + const QList<LDPolygon>& polygonData() const; + void recountTriangles(); + void redo(); + void redoVertices(); + void reloadAllSubfiles(); + void removeFromSelection (LDObject* obj); + bool save (QString path = "", qint64* sizeptr = nullptr); + long savePosition() const; + void setDefaultName (QString value); + void setFrozen(bool value); + void setFullPath (QString value); + void setName (QString value); + void setSavePosition (long value); + void setTabIndex (int value); + bool swapObjects (LDObject* one, LDObject* other); + int tabIndex() const; + void undo(); + void vertexChanged (const Vertex& a, const Vertex& b); + + static QString shortenName (QString a); // Turns a full path into a relative path + +public slots: + void objectChanged(QString before, QString after); + +protected: + LDObject* withdrawAt(int position); + +private: + QString m_name; + QString m_fullPath; + QString m_defaultName; + EditHistory* m_history; + bool m_isFrozen = true; // Document may not be modified + bool m_verticesOutdated = true; + bool m_isBeingDestroyed = true; + bool m_needsRecache = true; // The next polygon inline of this document rebuilds stored polygon data. + long m_savePosition; + int m_tabIndex; + int m_triangleCount; + QList<LDPolygon> m_polygonData; + QMap<LDObject*, QSet<Vertex>> m_objectVertices; + QSet<Vertex> m_vertices; + QSet<LDObject*> m_selection; + DocumentManager* m_manager; +}; + +// Parses a string line containing an LDraw object and returns the object parsed. +LDObject* ParseLine (QString line);
--- a/src/ldobjectiterator.h Tue Feb 14 14:51:04 2017 +0200 +++ b/src/ldobjectiterator.h Tue Feb 14 14:52:01 2017 +0200 @@ -18,7 +18,7 @@ #pragma once #include "ldObject.h" -#include "ldDocument.h" +#include "lddocument.h" template<typename T> class LDObjectIterator
--- a/src/mainwindow.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/mainwindow.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -25,7 +25,7 @@ #include "main.h" #include "canvas.h" #include "mainwindow.h" -#include "ldDocument.h" +#include "lddocument.h" #include "miscallenous.h" #include "messageLog.h" #include "ui_mainwindow.h"
--- a/src/miscallenous.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/miscallenous.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -22,7 +22,7 @@ #include "main.h" #include "miscallenous.h" #include "mainwindow.h" -#include "ldDocument.h" +#include "lddocument.h" int gcd (int a, int b)
--- a/src/partdownloader.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/partdownloader.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -23,7 +23,7 @@ #include "partdownloadrequest.h" #include "ui_partdownloader.h" #include "mainwindow.h" -#include "ldDocument.h" +#include "lddocument.h" #include "glrenderer.h" #include "documentmanager.h"
--- a/src/partdownloader.h Tue Feb 14 14:51:04 2017 +0200 +++ b/src/partdownloader.h Tue Feb 14 14:52:01 2017 +0200 @@ -20,7 +20,7 @@ #include <QDialog> #include "main.h" #include "basics.h" -#include "ldDocument.h" +#include "lddocument.h" class LDDocument; class QFile;
--- a/src/primitives.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/primitives.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -17,7 +17,7 @@ */ #include <QApplication> -#include "ldDocument.h" +#include "lddocument.h" #include "mainwindow.h" #include "primitives.h" #include "miscallenous.h"
--- a/src/toolsets/algorithmtoolset.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/toolsets/algorithmtoolset.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -24,7 +24,7 @@ #include <QSpinBox> #include "../mainwindow.h" #include "../main.h" -#include "../ldDocument.h" +#include "../lddocument.h" #include "../miscallenous.h" #include "../radioGroup.h" #include "../glrenderer.h"
--- a/src/toolsets/basictoolset.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/toolsets/basictoolset.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -23,7 +23,7 @@ #include <QTextEdit> #include <QVBoxLayout> #include "../canvas.h" -#include "../ldDocument.h" +#include "../lddocument.h" #include "../ldObject.h" #include "../ldobjectiterator.h" #include "../mainwindow.h"
--- a/src/toolsets/extprogramtoolset.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/toolsets/extprogramtoolset.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -30,7 +30,7 @@ #include "../main.h" #include "../miscallenous.h" #include "../mainwindow.h" -#include "../ldDocument.h" +#include "../lddocument.h" #include "../radioGroup.h" #include "../editHistory.h" #include "../documentmanager.h"
--- a/src/toolsets/filetoolset.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/toolsets/filetoolset.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -19,7 +19,7 @@ #include <QFileDialog> #include <QMessageBox> #include "../canvas.h" -#include "../ldDocument.h" +#include "../lddocument.h" #include "../mainwindow.h" #include "../partdownloader.h" #include "../primitives.h"
--- a/src/toolsets/movetoolset.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/toolsets/movetoolset.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -16,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "../ldDocument.h" +#include "../lddocument.h" #include "../mathfunctions.h" #include "../miscallenous.h" #include "../mainwindow.h"
--- a/src/toolsets/viewtoolset.cpp Tue Feb 14 14:51:04 2017 +0200 +++ b/src/toolsets/viewtoolset.cpp Tue Feb 14 14:52:01 2017 +0200 @@ -19,7 +19,7 @@ #include <QFileDialog> #include <QInputDialog> #include "../mainwindow.h" -#include "../ldDocument.h" +#include "../lddocument.h" #include "../miscallenous.h" #include "../canvas.h" #include "../primitives.h"