Fri, 23 Mar 2018 12:51:18 +0200
Begin rework to add support for multiple libraries
--- a/CMakeLists.txt Tue Mar 20 12:25:52 2018 +0200 +++ b/CMakeLists.txt Fri Mar 23 12:51:18 2018 +0200 @@ -43,6 +43,7 @@ src/hierarchyelement.cpp src/lddocument.cpp src/ldpaths.cpp + src/librariesmodel.cpp src/main.cpp src/mainwindow.cpp src/mathfunctions.cpp @@ -114,6 +115,7 @@ src/lddocument.h src/ldobjectiterator.h src/ldpaths.h + src/librariesmodel.h src/macros.h src/main.h src/mainwindow.h
--- a/src/basics.cpp Tue Mar 20 12:25:52 2018 +0200 +++ b/src/basics.cpp Fri Mar 23 12:51:18 2018 +0200 @@ -378,3 +378,13 @@ one = one ^ other; return one; } + +QDataStream& operator<<(QDataStream& out, const Library& library) +{ + return out << library.path << library.role; +} + +QDataStream& operator>>(QDataStream &in, Library& library) +{ + return in >> library.path >> enum_cast<>(library.role); +}
--- a/src/basics.h Tue Mar 20 12:25:52 2018 +0200 +++ b/src/basics.h Fri Mar 23 12:51:18 2018 +0200 @@ -27,6 +27,7 @@ #include <QFile> #include <QMatrix4x4> #include <functional> +#include <type_traits> #include <math.h> #include "macros.h" #include "transform.h" @@ -265,6 +266,15 @@ }; /* + * Casts a reference to an enum into a reference to its underlying integer type. + */ +template<typename T> +typename std::underlying_type<T>::type& enum_cast(T& enu) +{ + return *reinterpret_cast<typename std::underlying_type<T>::type*>(&enu); +} + +/* * Convenience function for RingAdapter so that the template parameter does not have to be provided. The ring amount is assumed * to be the amount of elements in the collection. */ @@ -283,6 +293,28 @@ return RingAdapter<T> {collection, count}; } +struct Library +{ + QString path; + enum + { + ReadOnlyStorage, // for official files, etc + UnofficialFiles, // put downloaded files here + WorkingDirectory, // for editable documents + } role = ReadOnlyStorage; + + bool operator==(const Library& other) const + { + return (this->path == other.path) and (this->role == other.role); + } +}; + +Q_DECLARE_METATYPE(Library) +using Libraries = QVector<Library>; + +QDataStream& operator<<(QDataStream& out, const Library& library); +QDataStream& operator>>(QDataStream& in, Library& library); + // // Get the amount of elements in something. //
--- a/src/colors.cpp Tue Mar 20 12:25:52 2018 +0200 +++ b/src/colors.cpp Fri Mar 23 12:51:18 2018 +0200 @@ -16,6 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <QDir> #include <QMessageBox> #include "colors.h" #include "ldpaths.h" @@ -29,6 +30,7 @@ void LDColor::initColors() { static ColorData colors; + colors.loadFromLdconfig(); LDColor::colorData = &colors; } @@ -216,9 +218,6 @@ m_data[EdgeColor].faceColor = Qt::black; m_data[EdgeColor].edgeColor = Qt::black; m_data[EdgeColor].name = "Edge color"; - - // Load the rest from LDConfig.ldr. - loadFromLdconfig(); } /* @@ -247,19 +246,41 @@ */ void ColorData::loadFromLdconfig() { - QString path = LDPaths::ldConfigPath(); - QFile file {path}; + *this = {}; - if (not file.open(QIODevice::ReadOnly)) + for (const Library& library : ::config->libraries()) { - QMessageBox::critical(nullptr, "Error", "Unable to open LDConfig.ldr for parsing: " + file.errorString()); - return; - } + QDir dir {library.path}; + + if (dir.exists("LDConfig.ldr")) + { + QFile file {dir.filePath("LDConfig.ldr")}; + if (file.open(QIODevice::ReadOnly)) + { + this->loadFromFile(file); + } + else + { + QMessageBox::critical( + nullptr, + QObject::tr("Error"), + format( + QObject::tr("Unable to open LDConfig.ldr for parsing: %1"), + file.errorString() + ) + ); + } + } + } +} + +void ColorData::loadFromFile(QIODevice& device) +{ // TODO: maybe LDConfig can be loaded as a Document? Or would that be overkill? - while (not file.atEnd()) + while (not device.atEnd()) { - QString line = QString::fromUtf8(file.readLine()); + QString line = QString::fromUtf8(device.readLine()); if (line.isEmpty() or line[0] != '0') continue; // empty or illogical
--- a/src/colors.h Tue Mar 20 12:25:52 2018 +0200 +++ b/src/colors.h Fri Mar 23 12:51:18 2018 +0200 @@ -37,6 +37,7 @@ ColorData(); void loadFromLdconfig(); + void loadFromFile(QIODevice& device); bool contains(int code) const; const Entry& get(int code) const;
--- a/src/configurationoptions.txt Tue Mar 20 12:25:52 2018 +0200 +++ b/src/configurationoptions.txt Fri Mar 23 12:51:18 2018 +0200 @@ -71,10 +71,10 @@ option DrawAngles = false # File management options +option Libraries = QVector<Library> {} option DownloadFilePath = "" option GuessDownloadPaths = true option AutoCloseDownloadDialog = true -option LDrawPath = "" # Other options option FirstStart = true
--- a/src/dialogs/configdialog.cpp Tue Mar 20 12:25:52 2018 +0200 +++ b/src/dialogs/configdialog.cpp Fri Mar 23 12:51:18 2018 +0200 @@ -33,6 +33,7 @@ #include <QPushButton> #include "../main.h" #include "../lddocument.h" +#include "../librariesmodel.h" #include "../miscallenous.h" #include "../canvas.h" #include "../guiutilities.h" @@ -74,9 +75,12 @@ QDialog (parent, f), HierarchyElement (parent), ui (*new Ui_ConfigDialog), - m_settings (MainWindow::makeSettings (this)) + m_settings (MainWindow::makeSettings (this)), + libraries {::config->libraries()}, + librariesModel {new LibrariesModel {this->libraries, this}} { ui.setupUi (this); + ui.librariesView->setModel(this->librariesModel); // Set defaults applyToWidgetOptions([&](QWidget* widget, QString confname) @@ -146,10 +150,52 @@ this, SLOT (buttonClicked (QAbstractButton*))); connect (ui.m_pages, SIGNAL (currentChanged (int)), this, SLOT (selectPage (int))); connect (ui.m_pagelist, SIGNAL (currentRowChanged (int)), this, SLOT (selectPage (int))); + connect( + this->ui.addLibrary, + &QPushButton::clicked, + [&]() + { + this->librariesModel->insertRow(this->librariesModel->rowCount()); + } + ); + connect( + this->ui.removeLibrary, + &QPushButton::clicked, + [&]() + { + QModelIndex index = this->ui.librariesView->currentIndex(); + + if (index.isValid()) + this->librariesModel->removeRow(index.row()); + } + ); + connect( + this->ui.moveLibraryUp, + &QPushButton::clicked, + [&]() + { + QModelIndex index = this->ui.librariesView->currentIndex(); + + if (index.isValid()) + this->librariesModel->moveRows({}, index.row(), 1, {}, index.row() - 1); + } + ); + connect( + this->ui.moveLibraryDown, + &QPushButton::clicked, + [&]() + { + QModelIndex index = this->ui.librariesView->currentIndex(); + + if (index.isValid()) + this->librariesModel->moveRows({}, index.row(), 1, {}, index.row() + 2); + } + ); } ConfigDialog::~ConfigDialog() { + delete this->librariesModel; delete &ui; } @@ -277,6 +323,7 @@ // Rebuild the quick color toolbar m_window->setQuickColors (quickColors); m_config->setQuickColorToolbar (quickColorString()); + ::config->setLibraries(this->libraries); // Ext program settings for (int i = 0; i < NumExternalPrograms; ++i)
--- a/src/dialogs/configdialog.h Tue Mar 20 12:25:52 2018 +0200 +++ b/src/dialogs/configdialog.h Fri Mar 23 12:51:18 2018 +0200 @@ -74,6 +74,8 @@ ExternalProgramWidgets m_externalProgramWidgets[NumExternalPrograms]; class QSettings* m_settings; QVector<ColorToolbarItem> quickColors; + class LibrariesModel* librariesModel; + Libraries libraries; void applySettings(); void addShortcut (QAction* act);
--- a/src/dialogs/configdialog.ui Tue Mar 20 12:25:52 2018 +0200 +++ b/src/dialogs/configdialog.ui Fri Mar 23 12:51:18 2018 +0200 @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>792</width> - <height>408</height> + <width>808</width> + <height>499</height> </rect> </property> <property name="windowTitle"> @@ -41,6 +41,11 @@ </item> <item> <property name="text"> + <string>Libraries</string> + </property> + </item> + <item> + <property name="text"> <string>Editing tools</string> </property> </item> @@ -351,6 +356,71 @@ </item> </layout> </widget> + <widget class="QWidget" name="page_2"> + <layout class="QVBoxLayout" name="verticalLayout_11"> + <item> + <widget class="QGroupBox" name="groupBox_8"> + <property name="title"> + <string>Libraries</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QTableView" name="librariesView"> + <attribute name="horizontalHeaderStretchLastSection"> + <bool>true</bool> + </attribute> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_12"> + <item> + <widget class="QPushButton" name="addLibrary"> + <property name="text"> + <string>Add</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="moveLibraryUp"> + <property name="text"> + <string>Move up</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="moveLibraryDown"> + <property name="text"> + <string>Move down</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="removeLibrary"> + <property name="text"> + <string>Remove</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_8"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </widget> <widget class="QWidget" name="page"> <layout class="QVBoxLayout" name="verticalLayout_6"> <item> @@ -1102,6 +1172,7 @@ </widget> <resources> <include location="../../ldforge.qrc"/> + <include location="../../ldforge.qrc"/> </resources> <connections/> </ui>
--- a/src/documentmanager.cpp Tue Mar 20 12:25:52 2018 +0200 +++ b/src/documentmanager.cpp Fri Mar 23 12:51:18 2018 +0200 @@ -17,6 +17,7 @@ */ #include <QApplication> +#include <QDir> #include <QFileInfo> #include <QMessageBox> #include "documentmanager.h" @@ -197,85 +198,22 @@ return path; } -QString DocumentManager::findDocumentPath (QString relativePath, bool subdirs) +QString DocumentManager::findDocument(QString name) const { - // 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 relativeTopDir = Basename (Dirname (relativePath)); - - for (LDDocument* document : m_documents) - { - QString partpath = format ("%1/%2", Dirname (document->fullPath()), relativePath); - QFileInfo fileinfo (partpath); - - if (fileinfo.exists()) - { - // Ensure we don't mix subfiles and 48-primitives with non-subfiles and non-48 - QString partTopDir = Basename (Dirname (partpath)); - - for (QString subdir : specialSubdirectories) - { - if ((partTopDir == subdir) != (relativeTopDir == subdir)) - goto skipthis; - } + name = name.replace("\\", "/"); - return partpath; - } -skipthis: - continue; - } - - if (QFileInfo::exists (relativePath)) - return relativePath; - - // Try with just the LDraw path first - QString fullPath = format ("%1" DIRSLASH "%2", m_config->lDrawPath(), relativePath); - - if (QFileInfo::exists (fullPath)) - return fullPath; + for (const Library& library : ::config->libraries()) + { + for (const QString& subdirectory : {"parts", "p"}) + { + QDir dir {library.path + "/" + subdirectory}; - 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_config->lDrawPath(), m_config->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; - } + if (dir.exists(name)) + return QDir::cleanPath(dir.filePath(name)); } } - // 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; + return {}; } void DocumentManager::printParseErrorMessage(QString message) @@ -283,65 +221,63 @@ print(message); } -LDDocument* DocumentManager::openDocument (QString path, bool search, bool implicit, LDDocument* fileToOverride) -{ - // 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; +LDDocument* DocumentManager::openDocument( + QString path, + bool search, + bool implicit, + LDDocument* fileToOverride +) { + if (search and not QFileInfo {path}.exists()) + { + // 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. + path = this->findDocument(path.toLower()); + } + + QFile file {path}; + + if (file.open(QIODevice::ReadOnly)) + { + LDDocument* load = fileToOverride; + + if (fileToOverride == nullptr) + load = m_window->newDocument(implicit); + + load->setFullPath(path); + load->setName(LDDocument::shortenName(path)); - if (search) - { - fp = openLDrawFile (path.toLower(), true, &fullpath); + // Loading the file shouldn't count as actual edits to the document. + load->history()->setIgnoring (true); + + Parser parser {file}; + Winding winding = NoWinding; + load->header = parser.parseHeader(winding); + load->setWinding(winding); + parser.parseBody(*load); + file.close(); + + if (m_loadingMainFile) + { + int numWarnings = 0; + + for (LDObject* object : load->objects()) + { + if (object->type() == LDObjectType::Error) + numWarnings += 1; + } + + m_window->changeDocument(load); + print(tr("File %1 opened successfully (%2 errors)."), load->name(), numWarnings); + } + + load->history()->setIgnoring (false); + return load; } else { - fp = new QFile (path); - fullpath = path; - - if (not fp->open (QIODevice::ReadOnly)) - { - delete fp; - return nullptr; - } + 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; - Parser parser {*fp}; - Winding winding = NoWinding; - load->header = parser.parseHeader(winding); - load->setWinding(winding); - parser.parseBody(*load); - fp->close(); - fp->deleteLater(); - - if (m_loadingMainFile) - { - int numWarnings = 0; - - for (LDObject* object : load->objects()) - { - if (object->type() == LDObjectType::Error) - numWarnings += 1; - } - - m_window->changeDocument (load); - print (tr ("File %1 parsed successfully (%2 errors)."), path, numWarnings); - } - - load->history()->setIgnoring (false); - return load; } void DocumentManager::addRecentFile (QString path)
--- a/src/documentmanager.h Tue Mar 20 12:25:52 2018 +0200 +++ b/src/documentmanager.h Fri Mar 23 12:51:18 2018 +0200 @@ -37,13 +37,17 @@ const Documents& allDocuments() const { return m_documents; } void clear(); LDDocument* createNew(); + QString findDocument(QString name) const; 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 = nullptr); - QFile* openLDrawFile (QString relpath, bool subdirs, QString* pathpointer); + LDDocument* openDocument( + QString path, + bool search, + bool implicit, + LDDocument* fileToOverride = nullptr + ); void openMainModel (QString path); bool preInline (LDDocument* doc, Model& model, bool deep, bool renderinline);
--- a/src/lddocument.cpp Tue Mar 20 12:25:52 2018 +0200 +++ b/src/lddocument.cpp Fri Mar 23 12:51:18 2018 +0200 @@ -67,12 +67,12 @@ QString LDDocument::name() const { - return m_name; + return this->header.name; } void LDDocument::setName (QString value) { - m_name = value; + this->header.name = value; } EditHistory* LDDocument::history() const
--- a/src/lddocument.h Tue Mar 20 12:25:52 2018 +0200 +++ b/src/lddocument.h Fri Mar 23 12:51:18 2018 +0200 @@ -130,7 +130,6 @@ LDObject* withdrawAt(int position); private: - QString m_name; QString m_fullPath; QString m_defaultName; EditHistory* m_history;
--- a/src/ldpaths.cpp Tue Mar 20 12:25:52 2018 +0200 +++ b/src/ldpaths.cpp Fri Mar 23 12:51:18 2018 +0200 @@ -26,7 +26,7 @@ m_config(config), m_dialog(nullptr) {} - +/* void LDPaths::checkPaths() { QString pathconfig = m_config->lDrawPath(); @@ -42,7 +42,7 @@ m_config->setLDrawPath(m_dialog->path()); } } - +*/ bool LDPaths::isValid (const QDir& dir) const {
--- a/src/ldpaths.h Tue Mar 20 12:25:52 2018 +0200 +++ b/src/ldpaths.h Fri Mar 23 12:51:18 2018 +0200 @@ -28,7 +28,7 @@ public: LDPaths(Configuration* config, QObject* parent = nullptr); - void checkPaths(); + // void checkPaths(); bool isValid (const class QDir& path) const; static QDir& baseDir(); @@ -43,4 +43,4 @@ Configuration* m_config; mutable QString m_error; class LDrawPathDialog* m_dialog; -}; \ No newline at end of file +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/librariesmodel.cpp Fri Mar 23 12:51:18 2018 +0200 @@ -0,0 +1,156 @@ +#include "librariesmodel.h" +#include "generics/migrate.h" + +LibrariesModel::LibrariesModel(Libraries& libraries, QObject* parent) : + QAbstractTableModel {parent}, + libraries {libraries} {} + +QString libraryRoleName(decltype(Library::role) role) +{ + switch (role) + { + case Library::ReadOnlyStorage: + return QObject::tr("Storage"); + + case Library::UnofficialFiles: + return QObject::tr("Unofficial files"); + + case Library::WorkingDirectory: + return QObject::tr("Working directory"); + + default: + return ""; + } +} + +int LibrariesModel::rowCount(const QModelIndex&) const +{ + return this->libraries.size(); +} + +int LibrariesModel::columnCount(const QModelIndex&) const +{ + return 2; +} + +QVariant LibrariesModel::data(const QModelIndex& index, int role) const +{ + Column column = static_cast<Column>(index.column()); + + if (index.row() >= 0 and index.row() < this->rowCount()) + { + const Library& library = this->libraries[index.row()]; + + switch (column) + { + case PathColumn: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: + return library.path; + } + break; + + case RoleColumn: + switch (role) + { + case Qt::DisplayRole: + return libraryRoleName(library.role); + + case Qt::EditRole: + return library.role; + } + break; + } + } + + return {}; +} + +bool LibrariesModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if (index.row() >= 0 and index.row() < this->rowCount({}) and role == Qt::EditRole) + { + Library& library = this->libraries[index.row()]; + Column column = static_cast<Column>(index.column()); + + switch (column) + { + case PathColumn: + library.path = value.toString(); + return true; + + case RoleColumn: + { + int intValue = value.toInt(); + + if (intValue >= 0 and intValue < 3) + { + library.role = static_cast<decltype(Library::role)>(intValue); + return true; + } + } + break; + } + } + + return false; +} + +Qt::ItemFlags LibrariesModel::flags(const QModelIndex& index) const +{ + Qt::ItemFlags flags = QAbstractTableModel::flags(index); + + if (index.isValid()) + flags |= Qt::ItemIsEditable; + + return flags; +} + +bool LibrariesModel::moveRows( + const QModelIndex&, + int sourceRow, + int count, + const QModelIndex&, + int destinationRow +) { + int sourceRowLast = sourceRow + count - 1; + this->beginMoveRows({}, sourceRow, sourceRowLast, {}, destinationRow); + ::migrate(this->libraries, sourceRow, sourceRowLast, destinationRow); + this->endMoveRows(); + return true; +} + +bool LibrariesModel::removeRows(int row, int count, const QModelIndex&) +{ + if (row >= 0 and row + count - 1 < this->rowCount()) + { + this->beginRemoveRows({}, row, row + count - 1); + this->libraries.remove(row, count); + this->endRemoveRows(); + return true; + } + else + { + return false; + } +} + +bool LibrariesModel::insertRows(int startRow, int count, const QModelIndex&) +{ + if (startRow >= 0 and startRow <= this->rowCount()) + { + this->beginInsertRows({}, startRow, startRow + count - 1); + + for (int row : range(startRow, startRow + 1, startRow + count - 1)) + this->libraries.insert(row, {}); + + this->endInsertRows(); + return true; + } + else + { + return false; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/librariesmodel.h Fri Mar 23 12:51:18 2018 +0200 @@ -0,0 +1,29 @@ +#pragma once +#include <QAbstractTableModel> +#include "main.h" + +class LibrariesModel : public QAbstractTableModel +{ +public: + enum Column { RoleColumn, PathColumn }; + + LibrariesModel(Libraries& libraries, QObject* parent); + + QVariant data(const QModelIndex& index, int role) const override; + int rowCount(const QModelIndex& parent = {}) const override; + int columnCount(const QModelIndex& parent = {}) const override; + bool setData(const QModelIndex& index, const QVariant& value, int role) override; + Qt::ItemFlags flags(const QModelIndex& index) const override; + bool moveRows( + const QModelIndex&, + int sourceRow, + int count, + const QModelIndex&, + int destinationRow + ) override; + bool removeRows(int row, int count, const QModelIndex&) override; + bool insertRows(int startRow, int count, const QModelIndex&) override; + +private: + Libraries& libraries; +};
--- a/src/main.cpp Tue Mar 20 12:25:52 2018 +0200 +++ b/src/main.cpp Fri Mar 23 12:51:18 2018 +0200 @@ -30,12 +30,18 @@ QApplication app (argc, argv); app.setOrganizationName (APPNAME); app.setApplicationName (APPNAME); + qRegisterMetaType<Library>("Library"); + qRegisterMetaType<Libraries>("Libraries"); + qRegisterMetaTypeStreamOperators<Library>("Library"); + qRegisterMetaTypeStreamOperators<Libraries>("Libraries"); static Configuration configObject; config = &configObject; + /* LDPaths* paths = new LDPaths(&configObject); paths->checkPaths(); paths->deleteLater(); + */ initializeCrashHandler(); LDColor::initColors();
--- a/src/toolsets/filetoolset.cpp Tue Mar 20 12:25:52 2018 +0200 +++ b/src/toolsets/filetoolset.cpp Fri Mar 23 12:51:18 2018 +0200 @@ -93,14 +93,6 @@ (new ConfigDialog {m_window})->exec(); } -void FileToolset::setLDrawPath() -{ - LDrawPathDialog* dialog = new LDrawPathDialog {m_config->lDrawPath(), true}; - - if (dialog->exec()) - m_config->setLDrawPath (dialog->path()); -} - void FileToolset::exit() { ::exit(EXIT_SUCCESS); @@ -155,10 +147,8 @@ { for (LDObject* obj : selectedObjects()) { - QString contents = obj->asText(); - QByteArray data = contents.toUtf8(); - file.write(data, countof(data)); - file.write("\r\n", 2); + file.write(obj->asText().toUtf8()); + file.write("\r\n"); } } else
--- a/src/toolsets/filetoolset.h Tue Mar 20 12:25:52 2018 +0200 +++ b/src/toolsets/filetoolset.h Fri Mar 23 12:51:18 2018 +0200 @@ -44,6 +44,5 @@ Q_INVOKABLE void saveAll(); Q_INVOKABLE void saveAs(); Q_INVOKABLE void scanPrimitives(); - Q_INVOKABLE void setLDrawPath(); Q_INVOKABLE void settings(); };
--- a/tools/configcollector.py Tue Mar 20 12:25:52 2018 +0200 +++ b/tools/configcollector.py Fri Mar 23 12:51:18 2018 +0200 @@ -59,7 +59,7 @@ except: pass - if endswith(value, 'f'): + if value.endswith('f'): try: float(value[:-1]) return 'float' @@ -76,22 +76,32 @@ def collect(self, filename): with open(filename) as file: - for line in file: - line = line.strip() - if line and not line.startswith('#'): - from re import search - match = search('^option (\w+) = (.+)$', line) - if not match: - raise ValueError('unable to parse: %r' % line) - name, value = match.groups() - match = search(r'^(\w+)\s*\{(.*)\}$', value) - try: - typename, value = match.groups() - if not value: - value = typename + ' {}' - except: - typename = deduce_type(value) - self.declare(name, typename, value) + for linenumber, line in enumerate(file, 1): + try: + line = line.strip() + if line and not line.startswith('#'): + from re import search + match = search('^option (\w+) = (.+)$', line) + if not match: + raise ValueError('unable to parse: %r' % line) + name, value = match.groups() + match = search(r'^([a-zA-Z0-9_<>]+)\s*\{(.*)\}$', value) + try: + typename, value = match.groups() + if not value: + value = typename + ' {}' + except: + typename = deduce_type(value) + self.declare(name, typename, value) + except ValueError as error: + from sys import stderr, exit + print(str.format( + '{file}:{line}: {error}', + file = filename, + line = linenumber, + error = str(error), + ), file = stderr) + exit(1) # Sort the declarations in alphabetical order self.declarations = OrderedDict(sorted(self.declarations.items(), key = lambda t: t[1]['name'])) # Fill in additional information